簡體   English   中英

遞歸宏 Elixir

[英]Recursive macro Elixir

我在玩 Elixir 宏——特別是那些自稱自己的宏,這是我在 Scheme 中經常做的事情。 我在下面創建了一個小測試宏,但是它只是掛起 iex - 沒有任何內容打印到控制台。 有沒有人知道為什么以及可以做些什么來糾正它?

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

編輯!!

好的,事實證明您可以定義遞歸宏,其中存在要匹配的結構差異(例如列表)。 以下是為我工作。 並在下面確認@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

我很高興能深入研究這一點,因為我學到了兩種語言的知識!

TL;DR:這是不可能的。


在宏是不是你所期望的是什么。 當編譯器看到一個宏,它在編譯時調用它注入它在那里它被稱為地方返回的AST。 也就是說,遞歸宏總是會導致編譯階段的無限循環。

IO.puts("something")放在quote do指令之前,您會看到它被無限打印,每次后續調用擴展宏時都會打印一次。

您可以使用@compile {:inline, test_rec: 1}來實現您所追求的行為。 為了更好地理解宏,您可能應該閱讀Elixir 指南中的Macros部分,特別是以下摘錄:

宏比普通的 Elixir 函數更難編寫,並且在不必要時使用它們被認為是不好的風格。 因此,請負責任地編寫宏。

實際上你可以做一些遞歸,但重點是想想你在做什么以避免在quote do [....] end調用宏。 您的 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

您會注意到兩件事,我確實在宏中使用遞歸,但在引用之外使用私有函數quote_value/1並且該函數具有在找到低於1值后“停止”的邏輯。 所有“引用”都放入列表中,技巧是將此列表放入元組{:__block__, [], put_quote_list_here}

現在請注意,如果 test_rec 的列表參數事先不知道(在編譯期間),則此宏將不會編譯,因此您需要調用宏test_rec(["a", "b", 0, 100, 200])以便編譯器知道大小和該列表的元素。

順便說一句,我使用了主體優化遞歸,但您可以輕松添加累加器並將其轉換為尾優化遞歸。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM