函数式编程的数学基础(五)再论lambda演算

898 阅读2分钟

我们前面的邱奇数推导中,已经掌握了一定的lambda演算技巧。接下来,是时候正式了解一下lambda演算了。

lambda的多种记法

因为时代原因,一些资料采用的lambda记法跟我们文章所写有一定区别,此处列出常见的记法。方便大家在查阅不同来源的资料时不至于混淆。

单字母命名lambda

在一些风格较为古老的资料中,只支持单字母参数名,这样,就会省略空格和点,λ\lambda符号后第一个字母为参数名,其后为表达式内容:

(λxλyxy)z(\lambda x\lambda yxy)z

以我们本系列的语法表示为:

(λx.λy.x y) z(\lambda x.\lambda y.x\ y)\ z

支持多参数

一些资料中,同样仅支持单字母作为lambda的参数名,但保留以点分割,这样可以支持柯里化的多参数记法,例如:

λxy.(x y)\lambda xy.(x\ y)

以我们本系列的语法表示为:

λx.λy.(x y)\lambda x.\lambda y.(x\ y)

二者完全等价,尽管书写形式上是多参数,lambda只支持柯里化的多参数。

符号替换记法

一些资料中,会用一些记法来表示lambda替换的过程。

[x:=y](x z)[x:=y](x\ z)

表示在后面的表达式中,把x替换为y。

另外一些资料中,也会用斜杠来表示:

[y/x](x z)[y/x](x\ z)

转换与归约规则

接下来我们来正式地介绍一下lambda演算的规则。

  • α转换:换元变换,直觉上,可以理解为当我们改变一个参数名时,只要用到此参数的部分都跟着改名,那么函数跟原来的函数等价。记为:
M α NM\ {\twoheadrightarrow}_{\alpha}\ N \\
λx.x α λy.y\lambda x.x\ {\twoheadrightarrow}_{\alpha}\ \lambda y.y
  • β归约:代入归约,直觉上,可以理解为我们调用一个函数时,把所有形参代换为实参进行化简。记为:
M β NM\ {\twoheadrightarrow}_{\beta}\ N \\
(λx.x z) y β (y z)(\lambda x.x\ z)\ y\ {\twoheadrightarrow}_{\beta}\ (y\ z)
  • η归约:调用归约,直觉上,可以理解为当一个函数内仅有另一个函数的调用,并透传参数时,实际上它们是同一个函数。记为:
M η NM\ {\twoheadrightarrow}_{\eta}\ N \\
λx.y x η y\lambda x.y\ x\ {\twoheadrightarrow}_{\eta}\ y

Church-Rosser定理

有时,一个lambda表达式存在多个不同的归约方式,那么先后归约哪一个呢?答案是,先后归约哪一个的结果都一样,这就要引入Church-Rosser定理了。

Church-Rosser定理的形式化定义:

M,N1,N2Λ:若有 MβN1 且 MβN2 则 XΛ:N1βX 且 N2βX\forall M, N_1, N_2 \in \Lambda: \text{若有}\ M\twoheadrightarrow_\beta N_1 \ \text{且}\ M\twoheadrightarrow_\beta N_2 \ \text{则}\ \exists X\in \Lambda: N_1\twoheadrightarrow_\beta X \ \text{且}\ N_2\twoheadrightarrow_\beta X

我们把它画成图:

MβN1ββN2βX\begin{array}{ccc} M & \xrightarrow{\text{β}} & N_1 \\ \downarrow \scriptstyle{β} & & \downarrow \scriptstyle{β} \\ N_2 & \xrightarrow{\text{β}} & X\\ \end{array}

我们对照图来解释,就是如果N1N_1N2N_2存在,那么一定能找到一个XX

直觉上,可以Church-Rosser定理理解为β归约的"殊途同归",即无论以何种顺序对lambda函数进行归约,最终可以得到一个一致的结果。

从直觉上来看,Church-Rosser定理的成立是显然的:对于一个“算式”来说,不论我们先计算它的哪个部分,最终都能得到同样的结果。

Lambda演算的Church-Rosser定理有几种不同的形式化证明方法,考虑到证明过程较为枯燥冗长,本篇就不在这里给出了,对数学有兴趣的的同学可以自行查阅相关资料。

Lambda演算的Church-Rosser定理还可以推广到η\eta归约,进而可以证明βη\beta\eta归约混合运算也符合Church-Rosser定理。

Church-Rosser定理揭示了lambda演算的重要性质:

  • 并行特性,具有多个归约方向lambda演算可以依据其结构进行并行运算,以lambda为基础理论的计算机语言也有此性质。
  • 推导一致性,不论如何进行归约,lambda演算不会得到自相矛盾的结果。

从Church-Rosser定理,我们很容易想到lambda表达式可以通过β归约,达到无法β归约的形式,这个形式被称为β正规形式。由Church-Rosser定理可知,若β正规形式存在,它必定是唯一的。

β正规形式可以被视为lambda演算的最终计算结果。

那么,是否每一个lambda函数都存在β正规形式呢?

不动点

我们来研究一个特殊的函数。

Ω=(λx.x x) (λx.x x)Ω = (λx. x\ x)\ (λx. x\ x)

当我们尝试对Ω做β归约时,可以发现,Ω的β归约是它自身。因此,Ω被称作β归约的不动点(fix point)。显然,不动点Ω不存在β正规形式。

当我们加入一次函数调用,Ω函数就变成了Y组合子,它能够用于实现函数递归。

Y=λf.(λx.f (x x)) (λx.f (x x))Y = λf.(λx.f\ (x\ x))\ (λx.f\ (x\ x))

我们也可以稍作变通,少写一个ff,把它写作:Y=λf.(λx.x x) (λx.f (x x))Y = λf.(λx.x\ x)\ (λx.f\ (x\ x)),。

这个形式与我们在除法一节推导出的Y组合子一致。经过一次β归约,就可以得到上面的形式。

具体到编程语言层面,考虑到Y组合子可能导致死循环,我们可以对ff的参数部分做逆η归约,形成ZZ组合子。

Z=λf.(λx.f (λz.x x z)) (λx.f (λz.x x z))Z = λf.(λx.f\ (λz.x\ x\ z))\ (λx.f\ (λz.x\ x\ z))

实际上,Y组合子的变体非常丰富,例如Haskell Curry本人发现的ΘΘ组合子。

Θ=λf.(λx.λy.f (y x)) (λx.λy.f (y x))Θ = λf.(λx.λy.f\ (y\ x))\ (λx.λy.f\ (y\ x))

尽管这些含有不动点的组合子破坏了β正规形式,但它们又是表达复杂计算逻辑的必备。

这也从侧面说明,我们无法通过机械进行β归约,去解决复杂的计算问题。

判定相等

既然β正规形式这条路走不通,那么是否有一种通用的方案,能够判定lambda表达式相等呢?

在一些情况下,这个问题非常简单,比如,如果两个lambda函数包含的符号和顺序完全一致(即文本意义上的相等),那它们两个必定等价;能够用三种规则互相转换的lambda表达式页总是互相等价的。

但对于含有不动点的lambda函数,可就没那么简单了。

此处我们不做严格证明,从感性的角度来思考一下这个问题的难度。

我们前面教程已经构造了分支逻辑和丘奇数基本运算,并且通过Y组合子可以实现递归,此处我们就使用大家更熟悉的符号系统来构造一个函数:

f=λx.{1if x==1f(x3+1)if x%2 != 0,f(x/2)if x%2==0. f = \lambda x. \left\{ \begin{array}{lll} 1 & \text{if } x == 1 \\ f(x * 3 + 1) & \text{if } x\%2\ !=\ 0, \\ f(x / 2) & \text{if } x\%2 == 0. \\ \end{array} \right.

我们来判断它是否与 λx.1\lambda x.1 等价。所以问题就转化成:对所有自然数x,如果它是奇数,就把它乘以三加一,如果它是偶数,则把它除以2,一直这样做下去,是否每个自然数都能变成1?

这个问题实质上等价于一个著名的未能解决的数学猜想:角谷静夫猜想(Collatz猜想)。

只要你愿意,还可以构造出歌德巴赫猜想等数学上的未决问题。

所以,我们可以得出结论:判定lambda表达式相等,其难度是大于或等于解决这些数学上的未决问题的。

对于一个lambda函数,其归约的结果可能是具有β正规形式的lambda函数,亦可能是无限递归的含有不动点的函数。

某些形式上含有不动点的函数,尽管形式上无法归约到具有β正规形式的函数,但又能用某些数学方法论证其等价于具有β正规形式的lambda函数。

判定两个lambda函数是否相等,实际上就是lambda版本的图灵停机问题。

正因为停机问题无法通过图灵机解决,lambda函数等价问题无法通过lambda函数归约求解,程序员才有机会不断发现更好的算法。