Julia 用局部变量计算表达式
我正在写一个遗传程序,以测试随机生成的表达式的适用性。这里显示的是生成表达式的函数以及主函数。DIV和GT在代码的其他地方定义:Julia 用局部变量计算表达式,julia,metaprogramming,Julia,Metaprogramming,我正在写一个遗传程序,以测试随机生成的表达式的适用性。这里显示的是生成表达式的函数以及主函数。DIV和GT在代码的其他地方定义: function create_single_full_tree(depth, fs, ts)
function create_single_full_tree(depth, fs, ts)
"""
Creates a single AST with full depth
Inputs
depth Current depth of tree. Initially called from main() with max depth
fs Function Set - Array of allowed functions
ts Terminal Set - Array of allowed terminal values
Output
Full AST of typeof()==Expr
"""
# If we are at the bottom
if depth == 1
# End of tree, return function with two terminal nodes
return Expr(:call, fs[rand(1:length(fs))], ts[rand(1:length(ts))], ts[rand(1:length(ts))])
else
# Not end of expression, recurively go back through and create functions for each new node
return Expr(:call, fs[rand(1:length(fs))], create_single_full_tree(depth-1, fs, ts), create_single_full_tree(depth-1, fs, ts))
end
end
function main()
"""
Main function
"""
# Define functional and terminal sets
fs = [:+, :-, :DIV, :GT]
ts = [:x, :v, -1]
# Create the tree
ast = create_single_full_tree(4, fs, ts)
#println(typeof(ast))
#println(ast)
#println(dump(ast))
x = 1
v = 1
eval(ast) # Error out unless x and v are globals
end
main()
我正在基于某些允许的函数和变量生成一个随机表达式。如代码中所示,表达式只能有符号x和v,以及值-1。我需要用各种x和v值测试表达式;这里我只是使用x=1和v=1来测试代码
表达式返回正确,但是eval()只能与全局变量一起使用,因此在运行时会出错,除非我将x和v声明为全局变量(错误:LoadError:UndevarError:x未定义)。如果可能的话,我会尽量避免使用全球化。有没有更好的方法来生成和计算这些使用本地定义变量生成的表达式?以下是生成(匿名)函数的示例。eval的结果可以作为函数调用,变量可以作为参数传递:
myfun = eval(Expr(:->,:x, Expr(:block, Expr(:call,:*,3,:x) )))
myfun(14)
# returns 42
dump
函数对于检查解析器创建的表达式非常有用。对于两个输入参数,可以使用元组,例如args[1]
:
julia> dump(parse("(x,y) -> 3x + y"))
Expr
head: Symbol ->
args: Array{Any}((2,))
1: Expr
head: Symbol tuple
args: Array{Any}((2,))
1: Symbol x
2: Symbol y
typ: Any
2: Expr
[...]
这有帮助吗?在Julia文档的部分中,在eval()
和effects部分下有一句话说
每个模块
都有自己的eval()
函数,用于在其全局范围内计算表达式
类似地,REPL帮助?eval
将在Julia 0.6.2上为您提供以下帮助:
计算给定模块中的表达式并返回结果。每个模块
(使用模块
定义的模块除外)都有自己的eval
单参数定义,用于计算该模块中的表达式
我假设您正在示例中的Main
模块中工作。这就是为什么需要在那里定义globals。对于您的问题,您可以使用宏
s并直接在宏内插入x
和y
的值
一个最简单的工作示例是:
macro eval_line(a, b, x)
isa(a, Real) || (warn("$a is not a real number."); return :(throw(DomainError())))
isa(b, Real) || (warn("$b is not a real number."); return :(throw(DomainError())))
return :($a * $x + $b) # interpolate the variables
end
在这里,@eval_line
宏执行以下操作:
Main> @macroexpand @eval_line(5, 6, 2)
:(5 * 2 + 6)
如您所见,宏
的参数值在宏中插入,表达式相应地提供给用户。当用户没有行为时
Main> @macroexpand @eval_line([1,2,3], 7, 8)
WARNING: [1, 2, 3] is not a real number.
:((Main.throw)((Main.DomainError)()))
在解析时向用户提供一条用户友好的警告消息,并在运行时抛出一个DomainError
当然,你也可以在函数中做这些事情,同样是通过插值变量——你不需要使用macro
s。但是,您希望最终实现的是将eval
与返回Expr
的函数的输出结合起来。这就是宏
功能的用途。最后,您只需在宏
名称前加上@
符号即可调用宏
s:
Main> @eval_line(5, 6, 2)
16
Main> @eval_line([1,2,3], 7, 8)
WARNING: [1, 2, 3] is not a real number.
ERROR: DomainError:
Stacktrace:
[1] eval(::Module, ::Any) at ./boot.jl:235
编辑1。您可以更进一步,并相应地创建函数:
macro define_lines(linedefs)
for (name, a, b) in eval(linedefs)
ex = quote
function $(Symbol(name))(x) # interpolate name
return $a * x + $b # interpolate a and b here
end
end
eval(ex) # evaluate the function definition expression in the module
end
end
然后,可以调用此宏,以稍后调用的函数的形式创建不同的行定义:
@define_lines([
("identity_line", 1, 0);
("null_line", 0, 0);
("unit_shift", 0, 1)
])
identity_line(5) # returns 5
null_line(5) # returns 0
unit_shift(5) # returns 1
编辑2。我想,您可以使用类似于下面的宏来实现您想要实现的目标:
macro random_oper(depth, fs, ts)
operations = eval(fs)
oper = operations[rand(1:length(operations))]
terminals = eval(ts)
ts = terminals[rand(1:length(terminals), 2)]
ex = :($oper($ts...))
for d in 2:depth
oper = operations[rand(1:length(operations))]
t = terminals[rand(1:length(terminals))]
ex = :($oper($ex, $t))
end
return ex
end
例如,这将给出以下结果:
Main> @macroexpand @random_oper(1, [+, -, /], [1,2,3])
:((-)([3, 3]...))
Main> @macroexpand @random_oper(2, [+, -, /], [1,2,3])
:((+)((-)([2, 3]...), 3))
感谢Arda的全面回复!这很有帮助,但我的一部分人认为可能有更好的方法来做到这一点,因为它似乎太迂回了。由于我正在编写一个遗传程序,我需要创建500个这样的AST,所有AST都带有随机函数和终端,这些函数和终端来自一组允许的函数和终端(代码中的fs和ts)。我还需要用20个不同的x和v值测试每个函数 为了利用您提供的信息实现这一点,我提出了以下宏:
macro create_function(defs)
for name in eval(defs)
ex = quote
function $(Symbol(name))(x,v)
fs = [:+, :-, :DIV, :GT]
ts = [x,v,-1]
return create_single_full_tree(4, fs, ts)
end
end
eval(ex)
end
end
然后,我可以在main()函数中提供500个随机函数名的列表,例如[“func1,func2,func3,…”。我可以在我的主函数中使用任何x和v值来计算。这解决了我的问题,但是,这似乎是一种非常迂回的方法,并且可能会使每次迭代都难以演化每个AST。也许可以使用输入参数创建函数的AST(而不是表达式的AST)?谢谢,请看下一篇博文。我没有说你应该定义500个不同的函数。我只是告诉你,如果你愿意,你可以用宏来定义函数(或者,具有相同原理的闭包)。请检查我修改过的答案。