[英]Using unquote_splicing in macros with modified lists
药剂的unquote_splicing
作品,未经时,直接unquoting通过列表的问题。 例如,调用下面的宏Test1.wrap([1,2,3])
将正确返回[0,0,0,1,2,3,0,0,0]
。
defmodule Test1 do
defmacro wrap(nums) do
quote do
[0,0,0, unquote_splicing(nums), 0,0,0]
end
end
end
但是,如果我对列表进行任何更改,然后尝试调用unquote_splicing
,Elixir 甚至不会让我定义宏:
defmodule Test2 do
defmacro double_wrap(nums) do
quote do
doubles = Enum.map(unquote(nums), & &1*2)
[0,0,0, unquote_splicing(doubles), 0,0,0]
end
end
end
这将直接引发编译错误:
warning: variable "doubles" does not exist and is being expanded to "doubles()", please use parentheses to remove the ambiguity or change the variable name
iex:37: Test.double_wrap/1
** (CompileError) iex:37: undefined function doubles/0
(elixir) src/elixir_locals.erl:108: :elixir_locals."-ensure_no_undefined_local/3-lc$^0/1-0-"/2
(elixir) src/elixir_locals.erl:108: anonymous fn/3 in :elixir_locals.ensure_no_undefined_local/3
到目前为止,我已经尝试了很多东西,例如:
bind_quoted
Macro
和Code
文档但没有任何效果,我无法弄清楚我做错了什么。
宏返回的内容被直接注入而不是调用代码。 Kernel.SpecialForms.unquote/1
(以及unquote_splicing/1
)用于获取访问调用者的上下文。 这就是您的代码引发的原因:在调用者上下文中没有定义局部变量doubles
。
您可以做的是在quote
块之外声明doubles
。
defmodule D do
defmacro double_wrap(nums) do
doubles = Enum.map(nums, & &1*2)
quote do
[0,0,0, unquote_splicing(doubles), 0,0,0]
end
end
end
require D
D.double_wrap [1,2,3]
#⇒ [0, 0, 0, 2, 4, 6, 0, 0, 0]
也就是说,这很高兴地解决了:
doubles = [1,2,3]
quote do: [0,0,0, unquote_splicing(doubles), 0,0,0]
#⇒ [0, 0, 0, 1, 2, 3, 0, 0, 0]
而这不是,因为在调用者上下文中没有doubles
:
quote do
doubles = [1,2,3]
[0,0,0, unquote_splicing(doubles), 0,0,0]
end
#⇒ ☠️ ** (CompileError) iex:7: undefined function doubles/0
错误消息说undefined function ,因为elixir尝试一个局部变量,如果它在当前上下文中没有找到它,它会尝试使用这个名称和零元调用函数。
有没有需要到达外quote
块检索的价值doubles
时doubles
被内部定义quote
块。 在引用块中定义的变量会自动将它们的值嵌入到 AST 中。 因此,您可以使用函数List.flatten() :
defmodule A do
defmacro double_wrap(nums) do
quote do
doubles = Enum.map(unquote(nums), & &1*2)
List.flatten [0,0,0, doubles, 0,0,0]
end
end
end
在 iex 中:
~/elixir_programs$ iex
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.8.2) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c "a.ex"
[A]
iex(2)> require A
A
iex(3)> A.double_wrap [1, 2, 3]
[0, 0, 0, 2, 4, 6, 0, 0, 0]
iex(4)>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.