Julia-Lang Metaprogramming: turn an expression into a function with expression-dependent arguments

Given a dictionary of meanings,

values ​​= {: A => 3 ,: B => 1}

rotate (arbitrary) expression like

expr =: (2 * A)

to the function foo (values) , which 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 take a slightly different approach, if necessary, as long as it can be automated.

Things I tried:

  • Conversion using convert (Function, expr) as suggested here . Crash for me (Julia 0.3.8-pre):

    convert does not have a matching method convert (:: Type {Function}, :: Expr)

  • Using @eval, you can do

    @eval foo (A) = $ (expr)

    and then call foo (values ​​[: A]), but this will require knowing that expr depends on A (and only on A).

  • I wrote the find_vars (exp) function to return the characters in expr (in this case [: A]), but I could not find how to use them in the @eval approach.

+7
dictionary expression metaprogramming julia-lang
source share
2 answers

Base.Cartesian has an outstanding function lreplace , which may be what you need. 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 determined by the values dict, it makes sense to define foo as a function with a null argument (unless I missed something).

EDIT: After re-reading your question, it seems like you want to go into the actual dictionary for the function, and not have the values ​​available at compile time, as I did above. In this case, we get a little creative:

First we need a lreplace function that will work with expressions that are light 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 each occurrence of a symbol in our keys with a dictionary search key

 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 
+7
source share

Thanks to @ptb's solution and another metaprogramming issue, I found a simpler and slower solution:

 function foo(values, expr) expr = quote A = values[:A] B = values[:B] return $(expr) end eval(expr) end 

Reading dictionary values ​​can also be done programmatically, replacing the internal rating with

  $([:($k = $v) for (k, v) in values]...) return $(expr) 
+1
source share

All Articles