简体   繁体   English

递归宏 Elixir

[英]Recursive macro Elixir

I am playing around with Elixir macros - specifically macros that call themselves which is something that I do often in Scheme.我在玩 Elixir 宏——特别是那些自称自己的宏,这是我在 Scheme 中经常做的事情。 I have created a little test macro below however it just hangs iex - nothing is printed to console.我在下面创建了一个小测试宏,但是它只是挂起 iex - 没有任何内容打印到控制台。 Does anyone have any insight into why and what could be done to correct it?有没有人知道为什么以及可以做些什么来纠正它?

defmodule RecMac do
  defmacro test_rec(x) do
    quote do
      IO.puts("Started?")
      if(unquote(x) < 1) do
        IO.puts("Done?")
        "done"
      else
        IO.puts("Where are we")
        IO.puts(unquote(x))
        RecMac.test_rec(unquote(x) - 1)
      end
    end
  end
end

EDIT!!编辑!!

OK, so it turns out you can define recursive macros where there is a structural difference to match on (eg lists).好的,事实证明您可以定义递归宏,其中存在要匹配的结构差异(例如列表)。 The following is working for me.以下是为我工作。 And to confirm @Aleksei Matiushkin below, the above will not work and does indeed not work in scheme!并在下面确认@Aleksei Matiushkin,上面的方法不起作用,并且在方案中确实不起作用!

  defmacro test_rec([h | t]) do
    quote do
      IO.inspect([unquote(h) | unquote(t)])
      RecMac.test_rec(unquote(t))
    end
  end

  defmacro test_rec([]) do
    quote do
      IO.puts "Done"
    end
  end
end

I am very happy to have dug into this as I learned something about two languages!我很高兴能深入研究这一点,因为我学到了两种语言的知识!

TL;DR: this is impossible. TL;DR:这是不可能的。


Macros in are not what you expect them to be.在宏是不是你所期望的是什么。 When a compiler sees a macro, it calls it during a compilation time and injects the AST it returned in the place of where it was called.当编译器看到一个宏,它在编译时调用它注入它在那里它被称为地方返回的AST。 That said, recursive macros would always lead to the infinite loop at the compilation stage.也就是说,递归宏总是会导致编译阶段的无限循环。

Put IO.puts("something") before quote do instruction and you'll see it to be printed infinitely, once per subsequent call to expand the macro.IO.puts("something")放在quote do指令之前,您会看到它被无限打印,每次后续调用扩展宏时都会打印一次。

You can use @compile {:inline, test_rec: 1} to achieve the behaviour you are after.您可以使用@compile {:inline, test_rec: 1}来实现您所追求的行为。 For better understanding macros, you probably should read Macros section in the Elixir Guide in general and this excerpt in particular:为了更好地理解宏,您可能应该阅读Elixir 指南中的Macros部分,特别是以下摘录:

Macros are harder to write than ordinary Elixir functions and it's considered to be bad style to use them when they're not necessary.宏比普通的 Elixir 函数更难编写,并且在不必要时使用它们被认为是不好的风格。 So write macros responsibly.因此,请负责任地编写宏。

Actually you can do kind of recursion, but point is to think what you are doing to avoid calling macro inside quote do [....] end .实际上你可以做一些递归,但重点是想想你在做什么以避免在quote do [....] end调用宏。 Your example with IO.puts would be something like this您的 IO.puts 示例将是这样的

defmodule RecMac  do
  defmacro test_rec(list) do
    {:__block__, [], quote_value(list)}
  end

  defp quote_value([]), do: []
  defp quote_value([h | t]) do
    if h < 1 do
      []
    else
      ast = quote do
         IO.puts("#{unquote(h)}")
      end
      [ast | quote_value(t)]
    end
  end
end

you will notice two things, I do use recursion in macro but outside quote using private function quote_value/1 and that function has logic that "stops" after it finds value lower than 1 .您会注意到两件事,我确实在宏中使用递归,但在引用之外使用私有函数quote_value/1并且该函数具有在找到低于1值后“停止”的逻辑。 All "quotes" are put into list and trick is to put this list into tuple {:__block__, [], put_quote_list_here}所有“引用”都放入列表中,技巧是将此列表放入元组{:__block__, [], put_quote_list_here}

Now note that this macro wont compile if list parameter of test_rec is not know upfront (during compile time), so you need to call macro test_rec(["a", "b", 0, 100, 200]) so compiler knows size and elements of that list.现在请注意,如果 test_rec 的列表参数事先不知道(在编译期间),则此宏将不会编译,因此您需要调用宏test_rec(["a", "b", 0, 100, 200])以便编译器知道大小和该列表的元素。

BTW, I used body optimized recursion, but you can easily add accumulator and convert this into tail optimized recursion.顺便说一句,我使用了主体优化递归,但您可以轻松添加累加器并将其转换为尾优化递归。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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