λ 演算
是 $\lambda$ 演算不是入演算[滑稽]
简介
lambda 表达式可以简单理解为匿名函数。
现在不少语言都加入了 lambda 表达式。就连名词王国 Java 也忍不住参与了。
一个普通的送命函数
1 | int Plus(int x) |
用 Java 中的 lambda 表达式是这样的
1 | x -> x + 1 |
用 JavaScript 中的 lambda 表达式是这样的
1 | x => x + 1 |
用 Scheme 中的 lambda 表达式是这样的
1 | (lambda (x) (+ x 1)) |
用 C++ 中的 lambda 表达式是这样的(没错就是要和别的妖艳贱货不一样)
1 | [=](int x){return x + 1;}; |
使用 lambda 表达式能够方便函数的创建,传递,返回等,大大方便了编程,就不说某不思进取语言之前只能传递匿名类的故事了。
实际上 lambda 表达式源于 $\lambda$ 演算。
对编程有基本了解的人都应该知道图灵机,图灵机是目前能实现的最强计算模型。图灵机的运算是读写头在纸带上运动,可以说是非常工程派了。相应地,学术派中用于研究计算的是 $\lambda$ 演算,它拥有图灵机一样的运算能力。
语法
$\lambda$ 演算的语法定义为
1 | <exp> ::= <var> |
第一条是变量抽象,即一个 $\lambda$ 项可以是一个变量。
第二条是函数抽象,即一个 $\lambda$ 项可以接收一个变量,返回一个 $\lambda$ 项。
第三条是函数应用,即一个 $\lambda$ 项可以应用一个 $\lambda$ 项。
比如对于 JavaScript 的 lambda 表达式 x => x + 1
可以表示为 $\lambda x. x + 1$ 。(假设 +
和 $1$ 都是良定的)。
第二条函数抽象限制了函数只有一个参数,那么遇到多个参数怎么办呢?
这里需要用到老生常谈的柯里化。
简单的说是将多参函数转化成多个单参函数的组合。
例如对于 (x, y) => x + y
相应的 $\lambda$ 项是 $\lambda x. (\lambda y. x + y)$ 。
为了手写方便,可以省略括号 $\lambda x . \lambda y . x + y$ 。
再次为了手写方便,可以省略为 $\lambda x y . x + y$ 。
规约
光有语法肯定是不够的。还需要规约规则。
$\lambda$ 演算有三条规约规则。
$\alpha$ 替换(重命名):替换被绑定变量不改变结果。如 $\lambda x. x + 1$ 和 $\lambda t . t + 1$ 是一样的。类似于编程语言中的函数参数重命名。
$\beta$ 规约(函数应用):函数应用为函数形参替换为实参的过程。如 $((\lambda x. x + 1) 2)$ 的结果为 $2 + 1 = 3$ 。类似于编程语言中的函数应用。
$\eta$ 转换(外延性相等):两个函数对于所有的参数得到的结果都一致。之所以称为外延性相等是如果把两个函数当作黑盒,那么它们是一致的。如果查看黑盒中的内容就会发现内容可能不一样。如 $\lambda x . f x$ 与 $f$ 外延性相等。举一个 JavaScript 的例子
1 | [1,2,3].forEach(x => console.log(x)) |
例子
说了那么多可能只是知道一些概念,可以在线运算一些例子来加深理解。
想要更加深入了解可以前往维基百科。