作者:红星闪闪的电灯泡 | 来源:互联网 | 2023-09-06 10:38
Elixir的unquote_splicing
在直接取消引用已传递列表的引用时不会出现问题。例如,调用Test1.wrap([1,2,3])
下的宏将正确返回[0,1,3,0]
。
defmodule Test1 do
defmacro wrap(nums) do
quote do
[0,unquote_splicing(nums),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,unquote_splicing(doubles),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
。
您可以做的是在doubles
块之外声明quote
。
defmodule D do
defmacro double_wrap(nums) do
doubles = Enum.map(nums,& &1*2)
quote do
[0,unquote_splicing(doubles),0]
end
end
end
require D
D.double_wrap [1,2,3]
#⇒ [0,4,6,0]
也就是说,这很高兴得到解决:
doubles = [1,3]
quote do: [0,0]
#⇒ [0,1,3,0]
这不是,因为在调用者上下文中没有doubles
:
quote do
doubles = [1,3]
[0,0]
end
#⇒ ☠️ ** (CompileError) iex:7: undefined function doubles/0
错误消息显示未定义的函数,因为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,doubles,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,3]
[0,0]
iex(4)>