我正在学习Elixir并尝试使用Macro系统 我试图使一个变量可用于一个宏,它将被其他代码包裹起来,但我显然做错了。
这是我的起点宏,它有效。我知道有更多惯用的方法可以在Elixir中进行迭代,我把它写成一个练习:
defmodule Loops do
defmacro times(n, [do: block]) do
quote do
Enum.each(1..unquote(n), fn(_) -> unquote(block) end)
end
end
end
import Loops
times 3 do
IO.puts "Hello!"
end
现在,我想要完成的是能够从do
块引用计数器。所以,如果我给fn
参数指定一个正确的名称:
Enum.each(1..unquote(n), fn(_) -> unquote(block) end)
# becomes:
Enum.each(1..unquote(n), fn(counter) -> unquote(block) end)
我希望能够做到这样的事情:
times 3 do
IO.puts "Hello! iteration n: #{counter}"
end
但由于CompileError
,这不起作用并引发undefined function counter/0
。从我传递给宏调用的do
块引发了错误,我觉得这有点违反直觉,因为我认为该块将unquote()
时扩展代码。
我是以错误的方式解决问题,还是这根本不可能?
答案 0 :(得分:3)
您可以使用Kernel.var!/2
使counter
成为“不卫生”变量。这将确保它可用于生成的代码,而无需由Elixir的宏系统重命名。
defmodule Loops do
defmacro times(n, [do: block]) do
quote do
Enum.each(1..unquote(n), fn(var!(counter)) ->
unquote(block)
end)
end
end
end
defmodule Main do
require Loops
def main do
Loops.times 3 do
IO.puts "Hello! iteration n: #{counter}"
end
end
end
Main.main
输出:
Hello! iteration n: 1
Hello! iteration n: 2
Hello! iteration n: 3