[英]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.