(确保您了解高阶函数)。在Alonzo Church的无类型 lambda 演算中,函数是唯一的原始数据类型。没有数字、布尔值、列表或其他任何东西,只有函数。函数只能有 1 个参数,但函数可以接受和/或返回函数——不是这些函数的值,而是函数本身。因此,要表示数字、布尔值、列表和其他类型的数据,您必须想出一种巧妙的方法让匿名函数代表它们。教会数字是表示自然数的方式。无类型 lambda 演算中三个最原始的构造是:
λx.x
,一个身份函数,接受一些函数并立即返回它。
λx.x x
,自行申请。
λf.λx.f x
, 函数应用,接受一个函数和一个参数,并将一个函数应用于一个参数。
您如何将 0、1、2 编码为函数?我们需要以某种方式将数量的概念构建到系统中。我们只有函数,每个函数只能应用于 1 个参数。我们在哪里可以看到类似数量的东西?嘿,我们可以多次将函数应用于参数!一个函数的 3 次重复调用显然有一种量感:f (f (f x))
. 所以让我们用 lambda 演算对其进行编码:
- 0 =
λf.λx.x
- 1 =
λf.λx.f x
- 2 =
λf.λx.f (f x)
- 3 =
λf.λx.f (f (f x))
等等。但是你如何从 0 到 1,或从 1 到 2?你将如何编写一个函数,给定一个数字,返回一个加 1 的数字?我们在 Church 数字中看到了该术语始终以f开头λf.λx.
和之后的模式,因此我们需要以某种方式进入 of 的主体并将其包装到另一个中。如何在不减少的情况下更改抽象体?好吧,您可以应用一个函数,将主体包装在一个函数中,然后将新主体包装到旧的 lambda 抽象中。但是您不希望参数改变,因此您将抽象应用于同名的值:, 但是,这不是我们需要的。λf.λx.
f
((λf.λx.f x) f) x → f x
((λf.λx.f x) a) b) → a b
add1
这就是为什么λn.λf.λx.f ((n f) x)
:你应用n
到f
然后x
将表达式减少到身体,然后应用f
到那个身体,然后再用 抽象它λf.λx.
。练习:也看到它是真的,快速学习β-reduction并将(λn.λf.λx.f ((n f) x)) (λf.λx.f (f x))
2 减为 1。
现在了解将主体包装到另一个函数调用背后的直觉,我们如何实现 2 个数字的加法?我们需要一个函数,给定λf.λx.f (f x)
(2) 和λf.λx.f (f (f x))
(3),将返回λf.λx.f (f (f (f (f x))))
(5)。看 2。如果你可以用 3 的主体替换它x
,那是f (f (f x))
什么?要获得 3 的主体,很明显,只需将其应用于f
然后x
。现在将 2 应用于f
,然后将其应用于 3 的主体,而不是x
。然后再把它包λf.λx.
起来:λa.λb.λf.λx.a f (b f x)
.
结论:要将 2 个数字和加a
在一起b
,这两个数字都表示为 Church 数字,您想用 的主体替换 x
in ,这样+ = 。要做到这一点,请先申请,然后申请。a
b
f (f x)
f (f (f x))
f (f (f (f (f x))))
a
f
b f x