如何递归调用匿名函数,这个问题困扰我好久了。直到我据说了 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