宏与接受表达式的函数

Macros vs functions that accept expressions

当我尝试以下代码片段时,我发现找不到变量 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

更笼统地说,接受表达式作为参数的函数和宏有什么区别?

传递给函数的表达式只是在 运行 时处理的正常值。代码在传递 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 循环中使用的变量名称匹配。