什么是 Y 组合子?
Y 组合子是一种高阶函数,它允许一个函数能够调用自身,即使这个函数本身没有名字。通常,递归函数需要通过引用自身来递归,但是 Y 组合子通过一种特殊的结构让匿名函数也可以递归。
Y 组合子的原理
在 lambda 表达式中,没有直接的变量赋值和函数定义(如 def
或者 let
)。Y 组合子的核心思想是通过一个自调用的高阶函数,让函数在没有名字的情况下自己调用自己。
Y 组合子的经典定义
Y = λf.(λx.f(x x))(λx.f(x x))
递归构造器:λf.(λx.f(x x))
lambda f
是一个高阶函数,接受一个函数f
作为参数。lambda x: f(x, x)
是一个返回递归函数的函数。这种构造允许我们将f
传递给自身,从而实现递归调用。
递归主体:(λx.f(x x))
一个简单例子:
def num_eights(n):
"""返回n中有几个8
>>> num_eights(3)
0
>>> num_eights(8888)
4
"""
if n == 0:
return 0
elif n % 10 == 8:
return 1 + num_eights(n // 10)
else:
return num_eights(n // 10)
用Y组合子的方式使它变成无名函数:
"""
>>> num_eights(3)
0
>>> num_eights(8888)
4
"""
# 0 if x == 0 else (1 + f(f, x // 10)) if x % 10 == 8 else f(f, x // 10)
num_eights = (lambda f: (lambda x: f(f,x))) (lambda f, x: 0 if x == 0 else (1 + f(f, x // 10)) if x % 10 == 8 else f(f, x // 10))
#或者这样写
num_eights = (lambda f: lambda x: 0 if x == 0 else (1 + f(f, x // 10)) if x % 10 == 8 else f(f, x // 10)) (lambda f, x: 0 if x == 0 else (1 + f(f, x // 10)) if x % 10 == 8 else f(f, x // 10))
这样我们可以看出:
(lambda x: f(f,x))
==
lambda x: 0 if x == 0 else (1 + f(f, x // 10)) if x % 10 == 8 else f(f, x // 10))
其中
f(f,x)
==
0 if x == 0 else (1 + f(f, x // 10)) if x % 10 == 8 else f(f, x // 10))
- 外层的
(lambda f: lambda x: f(f, x))
是 Y 组合子的构造器,它允许我们将f
作为参数传递给自身,实现递归。 - 内层的
lambda f, x: 0 if x == 0 else (1 + f(f, x // 10)) if x % 10 == 8 else f(f, x // 10)
是实际的递归函数体,它定义了如何处理递归逻辑。
关于 f
- 在
λx: f(f, x)
中,f
是外部lambda f
传递进来的函数。 f
代表的是λf, x: ...
这个函数。- 因此,
λx: f(f, x)
实际上是定义了如何调用f
这个函数,其中f
作为自身的递归调用。
具体解释
- 外部
lambda
实际上是将λf, x: ...
作为参数传递进来,使得f
可以在递归时使用。 - 内部
lambda
通过f(f, x)
调用外部函数,并且f
是递归的函数定义。