为说明起见,让我们说我有以下宏来计算真值表中的行:
macro bool_to_lit(a)
eval(a) ? (x -> x) : (x -> !x)
end
macro make_clause(xs, bools, res)
lits = map((x -> @eval @bool_to_lit $x), bools.args)
clause_elements = map.(lits, xs.args)
and_res = all(push!(clause_elements, res))
return and_res
end
#@make_clause((false, false), (false, false), true) returns true
@bool_to_lit
根据其自变量的值返回闭包,@make_clause
使用结果来计算其自身的值。但是,由于@make_clause
使用@eval
,所以我的理解是它实际上在运行@bool_to_lit
(因此,不仅执行语法转换)。
在这样的情况下避免使用嵌套的@eval
会更好(例如,更快并生成更清晰的代码),这样整个宏树的整个结果在rutime时仅被评估一次? / p>
是否在更容易编码( ie 在使用@eval
时将嵌套宏作为函数)和 vs。正确性( ie 避免嵌套的@eval
时仅编译时语法转换吗?
答案 0 :(得分:1)
(免责声明:我稍微缩短了代码的逻辑。这样做可能会出错,但总的观点是相同的。)
在大多数情况下,不,您不应该在宏中使用eval
。您可以选择两种替代方法。首先,如果您只要求宏仅对文字布尔值(即值true
和false
)起作用,则这些值将直接存储在AST中,您可以直接在编译时进行常规计算时间:
julia> macro make_clause_literal(xs, bools, res)
clause_elements = map((lit, arg) -> lit == arg, bools.args, xs.args)
res && all(clause_elements)
end
@make_clause_literal (macro with 1 method)
julia> @macroexpand @make_clause_literal((false, false), (false, false), true)
true
如果输入确实是文字布尔值,则应该添加一些检查。
另一方面,如果您想很好地放入其他表达式,请将代码转换为可以执行相同操作的高效代码,并将评估留给运行时:
julia> macro make_clause(xs, bools, res)
clause_elements = map((lit, arg) -> :($lit == $arg), bools.args, xs.args)
esc(:($res && $(foldr((e,f) -> :($e && $f), clause_elements))))
end
@make_clause (macro with 1 method)
julia> @macroexpand @make_clause((false, false), (false, false), true)
:(true && (false == false && false == false))
julia> @macroexpand @make_clause((false, false), (false, x), y)
:(y && (false == false && x == false))
构造&&
序列应该与避免中间阵列和短路方面的问题一样好。
我建议的第三种选择是编写一个执行子句评估的普通运行时函数,并根据对它的调用重写上面的任何一个宏。我将其保留为练习。您也可以将这两种方法结合起来,并在编译时尽可能地评估表达式,但是我猜编译器已经在某种程度上做到了。