如何递归调用匿名函数,这个问题困扰我很久了。直到我听说了 Y 组合子。
普通的递归函数是这样的:
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
的定义还没有完成。没关系,遇到不知道的东西,就把它作为参数吧。
所以我们修改了函数的定义,让它首先从参数 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)
这个东西替换掉,替换的原则是不改变运行时的执行逻辑:
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
有关的逻辑剥离出来了:
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
结合起来:
baz = fn x -> x.(x) end.((fn i -> fn g -> i.(fn x -> g.(g).(x) end) end end).(foo))
把 foo
提取出来:
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。