I'm writing a genetic program in order to test the fitness of randomly generated expressions. Shown here is the function to generate the expression as well a the main function. DIV and GT are defined elsewhere in the code:
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()
I am generating a random expression based on certain allowed functions and variables. As seen in the code, the expression can only have symbols x and v, as well as the value -1. I will need to test the expression with a variety of x and v values; here I am just using x=1 and v=1 to test the code.
The expression is being returned correctly, however, eval() can only be used with global variables, so it will error out when run unless I declare x and v to be global (ERROR: LoadError: UndefVarError: x not defined). I would like to avoid globals if possible. Is there a better way to generate and evaluate these generated expressions with locally defined variables?
答案 0 :(得分:1)
以下是生成(匿名)函数的示例。 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
[...]
这有帮助吗?
答案 1 :(得分:1)
在Julia文档的Metaprogramming部分, eval()
和效果部分下面有一句话说
每个
module
都有自己的eval()
函数,用于评估全局范围内的表达式。
同样,REPL帮助?eval
将在Julia 0.6.2上为您提供以下帮助:
评估给定模块中的表达式并返回结果。每个
Module
(除了用baremodule
定义的那些)都有自己的{-1}}的1参数定义,它定义该模块中的表达式。
我假设您正在使用示例中的eval
模块。这就是为什么你需要在那里定义全局变量。对于您的问题,您可以使用Main
s并直接在宏内插入macro
和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)
的参数的值在宏内插值,并且表达式相应地提供给用户。当用户不表现时,
macro
在 parse -time向用户提供了用户友好的警告消息,并在运行时抛出Main> @macroexpand @eval_line([1,2,3], 7, 8)
WARNING: [1, 2, 3] is not a real number.
:((Main.throw)((Main.DomainError)()))
。
当然,您可以在函数中执行这些操作,再次插入变量---您不需要使用DomainError
s。但是,您最终想要实现的是将macro
与返回eval
的函数的输出结合起来。这就是Expr
功能的用途。最后,您只需使用macro
名称前面的macro
号码呼叫@
:
macro
编辑1。您可以更进一步,并相应地创建功能:
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
然后,您可以调用此宏以函数的形式创建不同的行定义,以便稍后调用:
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
编辑2。我猜,您可以使用类似下面的宏来实现您想要实现的目标:
@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
将提供以下内容,例如:
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
答案 2 :(得分:0)
感谢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。