Макросы против функций, которые принимают выражения - PullRequest
3 голосов
/ 03 июня 2019

Когда я попробовал следующий фрагмент кода, я обнаружил, что переменная 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 Ответ

3 голосов
/ 03 июня 2019

Выражения, передаваемые в функции, являются просто обычными значениями, которые обрабатываются во время выполнения.Причина сбоя кода при его передаче 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 петля.

...