如何遞歸調用匿名函數,這個問題困擾我好久了。直到我據說了 Y 組合子。函數
普通的遞歸函數是這樣的:code
defmodule M do def foo(x) do case x do 0 -> 0 n -> foo(n-1) + n end end end
而後我一步步把它改形成匿名函數,首先,函數體大體不會變:遞歸
foo = fn x -> case x do 0 -> 0 n -> foo.(n-1) + n end end
這裏第二個 foo
的地方應該是 foo
這個函數自己被遞歸調用,然而這個時候 foo
的定義尚未完成。不要緊,遇到不知道的東西,就把它做爲參數吧。class
因此咱們修改了函數的定義,讓它首先從參數 g
接受它自己的定義,因爲 g
就是 bar
, 爲了獲得上面的本來的 foo
, 須要將它自己做爲參數傳遞給本身,用 g.(g)
來獲得 foo
。 這裏有點繞,可能須要多看幾遍。匿名函數
bar = fn g -> # 上一步裏的 foo 從這裏開始 fn x -> case x do 0 -> 0 n -> g.(g).(n-1) + n end end end baz = bar.(bar)
接下來想辦法先把 g.(g)
這個東西替換掉,替換的原則是不改變運行時的執行邏輯:module
bar = fn g -> h = fn x -> g.(g).(x) end fn f -> fn x -> case x do 0 -> 0 n -> f.(n-1) + n end end end.(h) end baz = bar.(bar)
如今能夠把和 foo
有關的邏輯剝離出來了:elixir
foo = fn f -> fn x -> case x do 0 -> 0 n -> f.(n-1) + n end end end bar = fn i -> fn g -> h = fn x -> g.(g).(x) end i.(h) end end.(foo) baz = bar.(bar)
最後將 bar
和 baz
結合起來:co
baz = fn x -> x.(x) end.((fn i -> fn g -> i.(fn x -> g.(g).(x) end) end end).(foo))
把 foo
提取出來:cas
baz = fn f -> fn x -> x.(x) end.( ( fn i -> fn g -> i.(fn x -> g.(g).(x) end) end end ).(f) ) end.(foo)
化簡內容:參數傳遞
baz = fn f -> fn x -> x.(x) end.( fn g -> f.(fn x -> g.(g).(x) end) end ) end.(foo)
替換一下參數名,最終咱們獲得 Y 組合子:
y = fn f -> fn x -> x.(x) end.(fn x -> f.(fn y -> x.(x).(y) end) end) end
使用一下試試看:
foo = y.(fn f -> fn x -> case x do 0 -> 0 n -> f.(n-1) + n end end end) foo.(5) # 15