Given a dictionary of values,
values = {:A => 3, :B => 1}
turn an (arbitrary) expression like
expr = :(2*A)
into a function foo(values) that evaluates the expression, so in this case foo(values) = 6. The resulting function will be called millions of times, so speed is an important consideration. I am happy to adopt a slightly different approach if necessary, as long as it can be automatised.
Things I tried:
The conversion using convert(Function, expr), as suggested here. Fails for me (Julia 0.3.8-pre):
convert
has no method matching convert(::Type{Function}, ::Expr)
Using @eval one can do
@eval foo(A) = $(expr)
and then call foo(values[:A]), but that would require knowing that expr depends on A (and only on A).
I wrote a function find_vars(exp) to return the symbols in expr (in this case [:A]), but couldn't find how to use them in the @eval approach.
Base.Cartesian has an unexported function lreplace
which may be what you're after. Then you can do something like:
julia> values = Dict(:A=>3, :B=>1)
Dict{Symbol,Int64} with 2 entries:
:B => 1
:A => 3
julia> import Base.Cartesian.lreplace
julia> expr = :(2*A)
:(2A)
julia> function lreplace_all(expr, d)
for (k, v) in d
expr = lreplace(expr, k, v)
end
expr
end
lreplace_all (generic function with 1 method)
julia> lreplace_all(expr, values)
:(2 * 3)
julia> @eval foo(A) = $(lreplace_all(:(2A), values))
foo (generic function with 1 method)
julia> foo(1)
6
Although, since A
is defined by the values
dict, it makes more sense to define foo as a zero-argument function (unless I've missed something).
EDIT: After rereading your question it seems like you want to pass in the actual dictionary to the function rather than have the values available at compile time as I've done above. In that case, we have get a little creative:
First we need an lreplace
like function that will work with expressions which is easy enough
julia> dictreplace!(ex, s, v) = ex
dictreplace! (generic function with 1 method)
julia> dictreplace!(ex::Symbol, s, v) = s == ex ? v : ex
dictreplace! (generic function with 2 methods)
julia> function dictreplace!(ex::Expr, s, v)
for i=1:length(ex.args)
ex.args[i] = dictreplace!(ex.args[i], s, v)
end
ex
end
dictreplace! (generic function with 3 methods)
julia> dictreplace(ex, s, v) = dictreplace!(copy(ex), s, v)
dictreplace (generic function with 1 method)
Now we want to replace every occurence of a symbol in our dict keys with a dictionary lookup
julia> function dictreplace_all(expr, kys, dsym)
for k in kys
expr = dictreplace(expr, k, :($(dsym)[$(QuoteNode(k))]))
end
expr
end
dictreplace_all (generic function with 1 method)
julia> dictreplace_all(:(2A), keys(values), :d)
:(2 * d[:A])
julia> @eval foo(args) = $(dictreplace_all(:(2A), keys(values), :args))
foo (generic function with 1 method)
julia> values[:A] = -99
-99
julia> foo(values)
-198
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With