ARM
ARM

Reputation: 1550

How to manipulate the value of variables based on an array of symbols in Julia?

This is somewhat related to David's question here. I'm interested in looping over an array of symbols (pointing to objects) and performing an operation on each step of the loop. For example, suppose I want to write a function that looks at a bunch of variables and converts any scalars to one-element arrays.

widget = "a scalar"
dohickey = ["item 1", "item 2"]
gizmo = "another scalar"

theVariables = [:widget, :dohickey, :gizmo]
for thisVariable in theVariables
    if !(typeof(eval(thisVariable)) <: Array)
        println("Found a scalar")
        #Make it a vector here 
    end
end

Ideally at the end of this we would have the following

widget = ["a scalar"]
dohickey = ["item 1", "item 2"]
gizmo = ["another scalar"]

This is a fairly lame example, but how can this be done? I thought I should be able to use something like

:($thisVariable) = [:($thisVariable)]

but I can't get it to work.

Edit: DSM's solution below works for the case described above, but in my actual usage I want to do this inside a function. If I define

function getsort(; arga="", argb="", argc="")
    filterSpecs = [:arga, :argb, :argc]
    for ts in filterSpecs
        if !(typeof($ts) <: Array)
            #nothing here
        end
    end
end

and then call getsort() it throws

error compiling getsort: error compiling __getsort#31__: syntax: prefix $ in non-quoted expression

I'm clearly missing something about metaprogramming magic.

Upvotes: 1

Views: 215

Answers (3)

Tom Breloff
Tom Breloff

Reputation: 1802

Are you sure you need a macro here? If you're looking to just handle your function inputs you can do something like:

julia> function newDictOfArrays(d)
         wrapped=false
         for (k,v) in d
           if !(typeof(v) <: AbstractArray)
             wrapped = true
             d[k] = [v]
           end
         end
         (wrapped, d)
       end
newDictOfArrays (generic function with 1 method)

julia> function f(; kwargs...)
         (wrapped, d) = newDictOfArrays(Dict(kwargs))
         if wrapped
           return f(;d...)
         end
         println("continue knowing all keyword args have been wrapped: $kwargs")
       end
f (generic function with 1 method)

julia> f(a=1, b=[2])
continue knowing all keyword args have been wrapped: Any[(:a,[1]),(:b,[2])]

julia> f(a=[1], b=[2])
continue knowing all keyword args have been wrapped: Any[(:a,[1]),(:b,[2])]

newDictOfArrays checks all members of a dictionary for values that are not subtypes of AbstractArray, and overrides that item with a wrapped value. In f, if anything was wrapped, it re-calls the same function again with the new dictionary passed as keyword arguments. No macros, eval, etc, although you could consider writing a macro that injects the code into f automatically if need this frequently.

Upvotes: 2

ptb
ptb

Reputation: 2148

This is more a followup to @DSM's answer but writing code in comments is hard. A word of caution: eval evaluates in the global scope which can lead to strange things

julia> a = 3
3

julia> function f(a)
         eval(:(a = 1))
         println(a)
       end
f (generic function with 1 method)

julia> f(a)
3

julia> a
1

Upvotes: 4

DSM
DSM

Reputation: 352989

From the code generation section of the docs, I think all you need is

    @eval $thisVariable = [$thisVariable]

For example:

widget = "a scalar"
dohickey = ["item 1", "item 2"]
gizmo = "another scalar"

theVariables = [:widget, :dohickey, :gizmo]
for thisVariable in theVariables
    if !(typeof(eval(thisVariable)) <: Array)
        println("Found a scalar")
        @eval $thisVariable = [$thisVariable]
    end
end

which gives me

Found a scalar
Found a scalar

julia> widget
1-element Array{ASCIIString,1}:
 "a scalar"

julia> dohickey
2-element Array{ASCIIString,1}:
 "item 1"
 "item 2"

julia> gizmo
1-element Array{ASCIIString,1}:
 "another scalar"

Upvotes: 3

Related Questions