讲给产品同学的λ演算入门课

讲给产品同学的λ演算入门课

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情

λ是希腊字母表的第11个字母,大写为Λ,英语名称为Lambda,中文读音“兰木达”。既然是谈演算,总得有数字吧,所以下面我们先来回顾一下什么是数字。

1 什么是数字

简单说,数字就是表示数的符号,比如阿拉伯数字“0,1,2,3”,汉语数字“〇,一,二,三”等等。如果你乐意,你也可以创造一套表示数的符号来。

下面我们来看看λ演算中的数字如何表示。

阿拉伯数字汉语数字λ数字
0λxy.y
1λxy.x(y)
2λxy.x(x(y))
3λxy.x(x(x(y)))

相信你一定发现规律了,λ的数字就是看λxy.后面自嵌套了多少次x(y),这里所谓“自嵌套”也可称作“递归”。递归是一个编程概念,就是一个函数把它的运行结果当作参数传入函数中再次运行。接下来我们再来看看什么是函数。

2 什么是函数

但凡读完九年义务教育的,对函数应该都不陌生。

f(x)=x+1就是一个函数,而且是个一元函数。我们把等号左边的f叫做函数名,括号里的x叫做自变量,等号右边的x+1叫做函数体或函数表达式。等号左边的整体f(x)叫做函数签名,签名包含了函数名和参数。

再举一例,g(x,y)=x+y是一个二元函数,其作用是对两个自变量x和y求和。
对比f(x)和g(x,y)这两个函数,共同点是都是做加法操作,不同的,是f(x)是对自变量加1,g(x,y)是用自变量x加上自变量y。所以其实可以用g(x,y)来代替f(x),只要把y替换为1就可以了,替换后是:g(x,1)=x+1,它和f(x)完全等价。

题外话:上面我们让二元函数的某个自变量固定为一个常量的操作称作柯里化(柯里是个人名),其效果是其二元函数降级为一元函数了。

那么在λ演算中怎么表示函数呢?举例来说,上面的f和g两个函数的λ演算可表示如下:
f(x)=x+1,表示为:λx.x+1 ; f(x,y)=x+y,表示为:λxy.x+y

看起来挺简单不是吗?然而上面的表示并不准确,因为在λ演算中并没有加号(+)这样的符号,对λ演算系统来说,符号就4种:①λ ②点 ③括号 ④变量字母。

你可能要问了,那怎么表示加减乘除这类运算呢?答案是用函数表示。举例来说上面的f(x)和g(x,y)在λ演算中其实就是加法操作,对应的函数是:λxyz.y(xyz)。

有点莫名其妙?没关系,看完下面的如何进行λ演算你就都清楚了。

3 进行λ演算

在λ演算系统中,一切皆函数,所谓演算就是用参数替换自变量,也就是我们常说的“代入”操作。只是这里代入的不仅可以是某个常量,还可以代入一个函数。

下面以加法函数λxyz.y(xyz)为例,来说明代入操作。

λxyz.y(xyz),对x代入a,结果是:λyz.y(ayz)

λxyz.y(xyz),对x,y分别代入a,b,结果是:λz.b(abz)

λxyz.y(xyz),对x,y,z分别代入a,b,c,结果是:b(abc)

是不是很简单?我们再增加一点难度。
λxyz.y(xyz),对x代入(λab.b),得到:λyz.y((λab.b)yz)。

关键的时候到了,注意λyz.y((λab.b)yz)括号内的(λab.b)yz,这种写法表明还可以继续进行代入操作,将yz代入λab.b后将得到z,y代替了a,z代替了b。最后再连上前面的λyz.y我们得到了λyz.y(z)

这里的“继续进行代入操作”的演算规则叫做“β规约”。

现在,对比一下前文关于λ数字的表示法,发现λyz.y(z)和1的λ表示法λxy.x(y)是不是很像?没错,它们仅仅是变量名不同而已。

在一个λ函数中更换其变量名并不影响函数的作用,这一规则叫做“α变换”。经过阿尔法变化后λyz.y(z)就等于λxy.x(y),也就是数字1的λ表示。

回头看看,我们对函数λxyz.y(xyz)中的x代入(λab.b),它的意义是不是就相当于对0进行了加1操作?

为了进一步说明λxyz.y(xyz)的作用(即加法操作),不妨设P=λxyz.y(xyz),我们把P视为加号(+),来验证一下1P2=3。

当然这里的1和2要使用λ数字表示法,同时为了避免和P的变量名xyz冲突,还要对1和2的λ表示法进行α变化,即用λab.a(b)λmn.m(m(n))代表1和2。那么,1P2的演算过程如下:
1P2
= (λab.a(b)) (λxyz.y(xyz)) (λmn.m(m(n))) //为了避免歧义这里加了3对括号
= λb.(λxyz.y(xyz))(b) (λmn.m(m(n))) // 第1步演算,用P代替1中的a变量
= (λxyz.y(xyz)) (λmn.m(m(n))) // 第2步演算,用λmn.m(m(n))代替b变量
= λyz.y((λmn.m(m(n)))yz) // 第3步演算,用λmn.m(m(n))代替x
= λyz.y(y(y(z)) // 第4步演算,用y代替m、z代替n
= 3 

4 总结

λ演算是一套形式化系统、一种计算模型,由数学家阿隆佐·邱奇在20世纪30年代首次发表。λ演算可以清晰地定义什么是一个可计算函数,任何可计算函数都能以这种形式表达和求值,它可以完全替代单一磁带图灵机。

λ演算的几个核心概念:
①变量:  写在符号λ和点(.)之间的字母。
②函数:  也叫抽象,即由符号λ、变量、点以及其后的表达式构成。
③应用:  由一个函数跟上要替换变量的参数构成,例如λab.a(b) C就是一个应用,其含义是将函数λab.a(b)应用于参数C,此应用的结果是λb.C(b)
④λ项:  对变量、函数、应用的统称,λ项是组成一行λ表达式的构成部分。

最后,所谓λ演算,就是构建λ项和对λ项进行归约化操作。在λ演算系统中,一切皆为函数,数字是函数,运算符是函数,运算结果也是函数,而所谓的运算其实就是“代入”操作,即对λ项进行归约化。