Julia-Lang Metaprogramming:将表达式转换为具有表达式依赖参数的函数

时间:2015-04-21 17:02:21

标签: dictionary expression metaprogramming julia

给出一个值词典,

  

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

转换(任意)表达式,如

  

expr =:(2 * A)

到一个函数 foo(values)来计算表达式,所以在这种情况下foo(values)= 6.结果函数将被调用数百万次,因此速度是一个重要的考虑因素。如果有必要,我很乐意采用略有不同的方法,只要它可以自动化。

我尝试的事情:

  1. 使用 convert(Function,expr)进行转换,如建议的here。失败了(Julia 0.3.8-pre):

      

    convert没有匹配convert(:: Type {Function},:: Expr)

    的方法
  2. 使用@eval可以做

      

    @eval foo(A)= $(expr)

    然后调用foo(values [:A]),但这需要知道expr取决于A(并且仅在A上)。

  3. 我写了一个函数 find_vars(exp)来返回expr中的符号(在本例中为[:A]),但是找不到如何在@eval中使用它们做法。

2 个答案:

答案 0 :(得分:7)

Base.Cartesian有一个未导出的函数lreplace,这可能就是你所追求的。然后你可以做类似的事情:

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

虽然Avalues dict定义,但将foo定义为零参数函数更有意义(除非我遗漏了一些东西)。

编辑: 重新阅读你的问题之后,似乎你想要将实际的字典传递给函数,而不是在编译时将值提供给我,就像我上面所做的那样。在这种情况下,我们会有一点创意:

首先,我们需要一个lreplace类似的函数,它可以使用足够简单的表达式

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)

现在我们想用字典查找替换dict键中每个符号的出现

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

答案 1 :(得分:1)

感谢@ptb和另一个metaprogramming question的解决方案,我找到了一个更简单但更慢的解决方案:

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

通过用

替换内部评估,也可以通过编程方式读取字典中的值
    $([:($k = $v) for (k, v) in values]...) 
    return $(expr)