Julia-Lang 元编程:将表达式转换为具有表达式相关参数的函数
Julia-Lang Metaprogramming: turn expression into function with expression-dependent arguments
给定一个值字典,
values = {:A => 3, :B => 1}
转一个(任意)表达式,如
expr = :(2*A)
到计算表达式的函数 foo(values) 中,因此在本例中 foo(values) = 6。生成的函数将被调用数百万次,因此速度是一个重要的考虑因素。如果有必要,我很乐意采用稍微不同的方法,只要它可以自动化即可。
我尝试过的事情:
转换使用 convert(Function, expr),如建议 here。对我来说失败了(Julia 0.3.8-pre):
convert
has no method matching convert(::Type{Function}, ::Expr)
使用@eval 可以做到
@eval foo(A) = $(expr)
然后调用 foo(values[:A]),但这需要知道 expr 依赖于 A(并且仅依赖于 A)。
我写了一个函数 find_vars(exp) 到 return expr 中的符号(在本例中是 [:A] ), 但找不到如何在 @eval 方法中使用它们。
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
虽然,由于 A
是由 values
字典定义的,将 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)
现在我们想用字典查找替换字典键中出现的每个符号
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 和另一个 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)
给定一个值字典,
values = {:A => 3, :B => 1}
转一个(任意)表达式,如
expr = :(2*A)
到计算表达式的函数 foo(values) 中,因此在本例中 foo(values) = 6。生成的函数将被调用数百万次,因此速度是一个重要的考虑因素。如果有必要,我很乐意采用稍微不同的方法,只要它可以自动化即可。
我尝试过的事情:
转换使用 convert(Function, expr),如建议 here。对我来说失败了(Julia 0.3.8-pre):
convert
has no method matching convert(::Type{Function}, ::Expr)使用@eval 可以做到
@eval foo(A) = $(expr)
然后调用 foo(values[:A]),但这需要知道 expr 依赖于 A(并且仅依赖于 A)。
我写了一个函数 find_vars(exp) 到 return expr 中的符号(在本例中是 [:A] ), 但找不到如何在 @eval 方法中使用它们。
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
虽然,由于 A
是由 values
字典定义的,将 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)
现在我们想用字典查找替换字典键中出现的每个符号
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 和另一个 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)