Dictionary Julia Lang元编程:将表达式转换为具有表达式相关参数的函数

Dictionary Julia Lang元编程:将表达式转换为具有表达式相关参数的函数,dictionary,expression,metaprogramming,julia,Dictionary,Expression,Metaprogramming,Julia,给定一本价值字典 值={:A=>3,:B=>1} 将(任意)表达式转换为 expr=:(2*A) 进入一个计算表达式的函数foo(values),因此在本例中foo(values)=6。结果函数将被调用数百万次,因此速度是一个重要的考虑因素。如果有必要,我很乐意采用稍微不同的方法,只要它可以自动化 我尝试过的事情: 按照建议,使用convert(函数,expr)进行转换。对我来说失败(Julia 0.3.8-pre): convert没有与convert(::Type{Function},::E

给定一本价值字典

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

将(任意)表达式转换为

expr=:(2*A)

进入一个计算表达式的函数foo(values),因此在本例中foo(values)=6。结果函数将被调用数百万次,因此速度是一个重要的考虑因素。如果有必要,我很乐意采用稍微不同的方法,只要它可以自动化

我尝试过的事情:

  • 按照建议,使用convert(函数,expr)进行转换。对我来说失败(Julia 0.3.8-pre):

    convert
    没有与convert(::Type{Function},::Expr)匹配的方法

  • 使用@eval可以做到

    @评估foo(A)=$(expr)

    然后调用foo(value[:A]),但这需要知道expr依赖于A(并且只依赖于A)

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


  • 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
    
    虽然,由于
    A
    是由
    values
    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
    

    多亏了@ptb和另一个解决方案,我找到了一个更简单但更慢的解决方案:

    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)
    

    我相信你的链接帖子是针对SymPy.jl的package@ptb:谢谢你指出这一点!我在Symphy软件包中找到了转换函数。在这种情况下没有帮助,但仅供参考,请参阅。这些函数非常不同,只有在程序执行期间
    expr
    从未更改时,才会产生相同的结果。我怀疑这两个函数都不是你想象的那样,因为你已经定义了你的函数来接受多余的参数(也就是说,foo应该只接受
    值,而fooauto应该是一个零参数函数。
    $
    中包装的项在
    @eval
    运行时进行计算,而不是在调用函数时进行计算。请尝试使用
    宏扩展(:(@eval…end))包装
    @eval
    块。)
    查看实际生成的函数。你完全正确!我必须在运行时添加一个外部函数调用来编译内部函数。当然,这会增加很多开销,因此现在只能执行10.000次调用/s。对于我的代码,这是可以的:我将在表达式中放入一个昂贵的For循环。@ptb:我分析了你的代码并获得了around 10.000.000 calls/s-太棒了!因此,对于给定的问题陈述,您的方法要好得多!此解决方案非常好!但是我有一个类似的问题,使用浮点而不是整数,
    lreplace
    不支持浮点。您知道如何解决它吗?奇怪的是
    lreplace
    不处理浮点但是上面的
    dictreplace!
    方法没有使用lreplace,所以它们应该可以正常工作