繁体   English   中英

在带有修改列表的宏中使用 unquote_spling

[英]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
  • 浏览MacroCode文档

但没有任何效果,我无法弄清楚我做错了什么。

宏返回的内容被直接注入而不是调用代码。 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 ,因为尝试一个局部变量,如果它在当前上下文中没有找到它,它会尝试使用这个名称和零元调用函数。

有没有需要到达外quote块检索的价值doublesdoubles被内部定义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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM