宏与接受表达式的函数

时间:2019-06-03 07:40:18

标签: macros julia metaprogramming

当我尝试以下代码片段时,我发现未找到变量i。为什么会这样?

function evalMyExpr(expr,n)
  for i in 1:n
    eval(expr)
  end
end

expr1 = Meta.parse("println(\"hello\")")
expr2 = Meta.parse("println(string(i))")

evalMyExpr(expr1,2) # ok
evalMyExpr(expr2,2) # UndefVarError: i not defined

请注意,如果我将其转换为宏,则可以使用:

macro evalMyExprMacro(expr,n)
  quote
    for i in 1:$n
      $expr
    end
  end
end

@evalMyExprMacro println(string(i)) 2 # ok

总的来说,接受表达式作为参数的函数和宏之间的区别是什么?

1 个答案:

答案 0 :(得分:3)

传递给函数的表达式只是在运行时处理的普通值。代码在传递expr2时失败的原因是,eval在全局范围内计算表达式(通常不建议在函数中使用eval)。因此,由于在您的情况下可能未在全局范围中定义变量i,因此会出现错误。请参见在全局范围内定义i时的示例:

julia> i = 1000
1000

julia> function evalMyExpr(expr,n)
         for i in 1:n
           eval(expr)
         end
       end
evalMyExpr (generic function with 1 method)

julia> expr2 = Meta.parse("println(string(i))")
:(println(string(i)))

julia>

julia> evalMyExpr(expr2,2)
1000
1000

现在-在marcos中,表达式是在编译时处理的(在运行代码之前),因此您使用的表达式将被注入到随后执行的宏所生成的代码中。您可以使用@macroexpand来看到效果:

julia> macro evalMyExprMacro(expr,n)
         quote
           for i in 1:$n
             $expr
           end
         end
       end
@evalMyExprMacro (macro with 1 method)

julia> @macroexpand @evalMyExprMacro println(string(i)) 2
quote
    #= REPL[23]:3 =#
    for #6#i = 1:2
        #= REPL[23]:4 =#
        (Main.println)((Main.string)(#6#i))
    end
end

观察到变量名称已由宏处理机制更改为#6#i,并且与for循环中使用的变量名称匹配。