lambda算子3:阿隆佐.丘齐(Alonzo Church)的天才

lambda算子3

阿隆佐.丘齐(Alonzo Church)的天才:Lambda算子里的数

前面建立了lambda运算的基本规则,就可以用lambda算子做点有意思的东西了。开始前为方便计,我们先来点语法糖花差花差,用来命名函数。这些语法糖可以让复杂的公式好写一点。

我们用"let" 来引入一个“全局”函数(也就是说,我们用这个函数时,不用在每个表达式里定义一次):
let squer = lambda x. x^2

这个式子申明了一个叫"square"的函数,定义为 lamdba x. x^2。如果我们有一个表达式 "square 4",上面的"let"意味着这个表达式和下面这个表达式一样:
(lambda square. square 4)(lambda x. x^2)。这个"let"是从Common Lisp或者Scheme里借来的。Lambda算子里可没有这个东西。数学家推崇“如无必要,毋增实体”。这些关键字不入他们的法眼。不过对写惯了程序的我们来说,这些句法糖就可爱多了。

我们的例子里会用到数字和算术操作符。不过记住lambda算子里根本没有数字。我们只有函数!所以我们需要发明用函数来创造数字的方法。幸好Alonzo Church是个天才。他既然发明了lambda算子,用lambda算子表征数字自然不在话下。他搞出的用于数字的函数自然就叫做丘齐数(Church Numerals)。

丘齐数里,所有的数字都是两个参数的函数:

  1. 零是 lambda s z . z
  2. 一是 lambda s z . s z
  3. 二是 lambda s z . s (s z)
  4. 对任意一个数"n",它的丘齐数都是一个函数。这个函数把它的第一个参数应用到第二个参数上n次。用流行的写法,就是lambda s z . s sn z。 绕口啊绕口。做形式化的东东不幸之处就是成天和绕口令打交道。解脱这道呢?当然就是牢记牛人费因曼在Connection Machine工作时的学习方法:问最简单的问题。“给我最简单的例子”。“怎么才能验证这是正确的?”。
    比如说零(lambda s z . z)吧,第一个参数是s, 应用零次就是没有,所以函数体就是孤零零的"z"。那数字一呢?当让就是把第一个参数,s,应用到z上一次,所以函数体就变成了"s z"。

理解这个定义的方法之一时把"z"看作丘齐数里零的名字,而把"s"看后继函数(successor function)的名字。“后继函数”其实很简单,C/C++里的++是也。所以呢,零就是一个返回"0"这个值的函数;一就是把后继函数应用到零上一次的函数;二就是把后继函数应用到一上一次或者说零上两次的函数。0++ 得到 1, 1++ 等价与(0++)++,而1++得到2.现在把0换成z,把++换成s, 一切就清楚了。


现在--看好了。如果我们想做加法,x+y,我们需要一个带4个参数的函数。两个参数代表相加的两个数字,以及为得到结果而需要的"s"和"z"。

let add = lambda s z x y . x s (y s z)

看着好像有点不知所云。不过我们可以用Curry这个利器,分开"s" "z"和x, y。首先,Curry后得到的函数带两个参数,x和y(这个好比add(x, y),符合我们对加号的理解)。其次,我们需要正规化x和y需要的s和z,让x和y共享相同的零和后继函数的绑定:

let add = lambda x y. (lambda s z . (x s (y s z)))

仔细观察一下,上面的式子无非是说,要把x和y相加,我们先用"s"和"z"创建丘齐数"y",然后在把"x"应用到y上。应用时需要的"s"和"z"是"y"里的"s"和"z"。也就是说,我们的到的结果是一个函数,这个函数把自己加到另一个函数上。还是用例子来说明问题。比如说2+3:

add (lambda s z. s (s z)) (lambda s z . s (s (s z))) news newz

为了让演算变得稍微容易一点,我们先对2和3来个Alpha转换。让2用s2和z2,而3用s3和z3:

add (lambda s2 z2 . s2 (s2 z2)) (lambda s3 z3 . s3 (s3 (s3 z3)))

现在我们可以把"add"替换成它的定义了:

(lambda x y .(lambda s z. (x s y (s z)))) (lambda s2 z2 . s2 (s2 z2)) (lambda s3 z3 . s3 (s3 (s3 z3))) 

现在可以对"add"用beta变换了(温馨提示:也就是把形参x和y换成对应的实参):

lambda s z . (lambda s2 z2 . s2 (s2 z2)) s (lambda s3 z3 . s3 (s3 (s3 z3)) s z)


然后我们可以对3这个丘齐数做beta转换。这步操作其实是“正规化”3:把3的定义里的后继函数和零函数(还记得零是个函数吧?)替换成add的参数列表里的后继函数和零函数:

lambda s z . (lambda s2 z2 . s2 (s2 z2)) s (s (s (s z)))

嗯,有点眉目了。现在是真正漂亮的地方了。再来次对2的Beta变换。看看我们准备做什么:2是个带两个参数的函数:一个参数是后继函数,另一个是零函数。要把2加到3上,我们只需要用到"add"这个函数的后继函数。也就是说,我们把计算了3后的结果当成零函数的值!

lambda s z . s (s (s (s (s z)))

而这个式子,正是丘齐数5!

丘齐数酷的地方是它抛弃了传统整数的概念,用函数取而代之。它把每个数对应为一个函数。而数数(counting)这个操作被对应为应用某个函数(在这里是后继函数)的次数。当然了,上面的介绍非常简单。对丘齐数感兴趣的,可以看这篇文章

丘齐数对编程有什么用嗫?俺还真不知道。但丘齐数(进而到丘齐编码)确实一系列基础理论中有重要应用,比如说有类型的lambda算子。不过这点重要吗?不重要吗?重要吗?不重要吗?研究研究嘛。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值