写在前面
学这个主要是因为我们社团准备要规范一些写代码的习惯以及结构 确定了OOP以及函数化编程后在寻找适合函数化编程的伪代码规范的时候决定恶补一下底层知识
本文适合对于计算机科学与代数有一定了解的同学 最好是python 我文中就先用py的lambda expression来做的implementation
我自己做了个λ-calculus的演算器,可以来玩玩 网站 GitHub
结尾有Cheatsheet哈~
考虑函数f(x)=x2. 我们可以把它写做一个函数:
f:x↦x2
Lambda演算就是完全围绕函数及其运算展开的,因此函数的定义是必要的。在Lambda演算中,函数被称为lambda抽象:
定义 1.0
λ抽象 (Lambda Abstraction) 表示单值匿名函数。对于映射关系 x↦M,其等价的λ抽象定义为:
λx. M
其中 x 称为绑定变量,M 称为函数体。在现代编程语言中,x 也被称作输入参数。
因此此前的函数可以写作:
λx. x2
主意
虽然它们的类型等价,但是 (λx. x2) 与下面这个函数不是一样的:
x2是这个抽象的函数体,是这个函数的值。在编程语言中,f(x)在未带入前是无意义的,但是在lambda演算中我们的抽象体是有具体意义的
- 变成语言中的函数更像是计算器,可以给你直接算出答案
- lambda抽象是草稿纸上的过程,它们保留了代数结构以便推导
值得注意的是Python等语言中也有定义匿名函数的方法:
(lambda x: x ** 2) # Defines a lambda function that squares
(lambda x: x ** 2) (2) # Outputs 4
抽象体可以参与运算。考虑先前的结构体:
它表示一个λ抽象 (λx. x2)。我们可以注意到,改变抽象的绑定变量并不会改变映射的本质。也就是说,无论抽象被命名为 (λy. y2) 还是 (λ⋆. ⋆2),它始终等价于我们最初的抽象。因此我们定义α转换如下:
定义
α转换是对λ抽象改变其绑定变量的操作。对于一个具有绑定变量x和函数体M的抽象:
(λx. M) →α (λy. M)
就是一个α转换。
以下是在我们之前的Python lambda表达式上进行α转换的示例:
>>> f1 = (lambda x: x ** 2) # 原始函数
>>> f2 = (lambda y: y ** 2) # 完成α转换
>>> assert f1(2) == f2(2) # 验证等价性
True
函数本身并不直接发挥作用。要实际运用函数,就需要进行函数应用:
定义
β规约是指使用λ抽象计算特定值的函数应用过程。对于具有绑定变量x和函数体M的抽象:
(λx. M)N →β M[N/x]
例如,考虑代数函数 f(x)=x2 计算 f(3),在λ演算中等价为:
(λx. x2) 3 →β x2[3/x]=32=9
让我们在前面的函数上实际验证:
>>> f = (lambda x: x ** 2)
>>> f(2) # 将2应用于参数x,即计算 x**2 [2/x]
4
需要注意的是,λ演算是完全符号化的,因此不允许直接进行数值运算。平方运算示例仅用于演示目的。
至此,λ抽象的一个限制显现出来:它们都是单参函数。然而,λ抽象的高阶特性允许它们接受函数作为输入。考虑以下二元加法的实现:
(λx. (λy. (x+y))) M N ↓β(λy. (x+y) [x/M]) N ↓β(x+y) [x/M] [y/N]=M+N
这种技术称为柯里化(Currying),以逻辑学家Haskell Curry的名字命名。括号仅用于便于理解;λ抽象本身可简写为:
λx. λy. x+y
实现
在Python中,柯里化的λ函数可以这样实现:
(lambda x: lambda y: x + y)(1)(2) # 输出3
尽管嵌套函数似乎要求严格的参数顺序,但在柯里化函数中并非如此。
以下是对引理证明和布尔值编码的专业中文翻译:
证明
引理. 形式为 f=λx. λy. M(x,y) 和 g=λy. λx. M(y,x) 的任意两个抽象是等价的。
证明. 对 f 和 g 进行α转换:
f=λx. λy. M(x,y)→αx→aλa. λy. M(a,y)→αy→bλa. λb. M(a,b)
g=λy. λx. M(y,x)→αy→aλa. λx. M(a,x)→αx→bλa. λb. M(a,b)
因此 f≡g。
目前的λ演算还相当原始。但根据邱奇-图灵论题(Church-Turing thesis),这种函数式编程语言是图灵完备的,因此能够实现任何现代编程语言的功能。
让我们看看现代语言中某些特性的λ演算实现。
在λ演算中,所有计算都通过函数抽象和应用来表达。我们可以将布尔值定义为选择函数:
True=λx. λy. xFalse=λx. λy. y
如果将这些λ函数转化为Python命名函数,其形式如下:
def true(x, y):
return x # 总是选择第一个参数
def false(x, y):
return y # 总是选择第二个参数
显然,这些函数实现了条件选择的功能。它们看起来像是硬编码的,但如果将它们视为变量,就能理解其通用性。
条件运算符可如下实现:
If=λb. λx. λy. b x y
规约示例:
If True M N →β (λb. λx. λy. b x y) True M N→β (λx. λy. True x y) M N→β (λy. True M y) N→β True M N→βTrue [M/x][N/y]→β(λx. λy. x) M N→β(λy. M) N→βM
If False M N →β (λb. λx. λy. b x y) False M N→β (λx. λy. False x y) M N→β (λy. False M y) N→β False M N→βFalse [M/x][N/y]→β(λx. λy. y) M N→β(λy. y) N→βN
通过上述规约过程,我们实现了有效的条件控制流。基于此原理,甚至可以创建具有有限分支的switch语句。例如,我们可以实现一个三路选择器:
首先定义分支选择函数:
A=λx. λy. λz. x(选择第一参数)B=λx. λy. λz. y(选择第二参数)C=λx. λy. λz. z(选择第三参数)
然后定义选择器核心:
Selector=λs. λx. λy. λz s x y z
选择器调用过程:
Selector A a b c Selector B a b c Selector C a b c →β A a b c=a→β B a b c=b→β C a b c=c
Python实现示例:
>>> A = (lambda x: lambda y: lambda z: x) # 总是返回第一个参数
>>> B = (lambda x: lambda y: lambda z: y) # 总是返回第二个参数
>>> C = (lambda x: lambda y: lambda z: z) # 总是返回第三个参数
>>> Selector = (lambda s: lambda x: lambda y: lambda z: s(x)(y)(z))
>>> Selector(A)('a')('b')('c') # 选择第一个分支
'a'
>>> Selector(B)('a')('b')('c') # 选择第二个分支
'b'
>>> Selector(C)('a')('b')('c') # 选择第三个分支
'c'
在定义了基本布尔值后,布尔运算符可以轻松实现。例如,"非"运算通过切换两个分支来实现:
Not=λs. If s False True
"与"运算则通过暴力检查绑定变量组合实现:
And=λa. λb. a b False
更进一步,If 语句可以被完全省略:
证明框
引理. 任何形式为 If s a b 的应用等价于 s a b。
证明. 对应用进行β规约:
If s a b→β((λb. λx. λy. b x y) [s/b]) a b→β(λx. λy. s x y) a b→βs a b
<证毕/>
其他二元运算符 (λa. λb. M) 可类似实现:
运算符 | 实现 M | 逻辑描述 |
---|
AND | a b False | a真时取b,否则取假 |
OR | a True b | a真时取真,否则取b |
XOR | a (Not b) b | a真时取非b,否则取b |
NAND | a (Not b) True | a真时取非b,否则取真 |
更多实现详见速查表
- λ抽象表示:
λa.λb.a
- Python实现:
T = lambda a: lambda b: a
- 效果:
选择第一个参数:T a b→a
- 示例:
- λ抽象表示:
λa.λb.b
- Python实现:
F = lambda a: lambda b: b
- 效果:
选择第二个参数:F a b→b
- 示例:
- λ抽象表示:
λs.s F T
- Python实现:
NOT = lambda s: s (F) (T)
- 效果:
¬T¬F→F→T
- 示例:
>>> NOT (T) ("真") ("假")
"假"
- λ抽象表示:
λa.λb.a b F
- Python实现:
AND = lambda a: lambda b: a (b) (F)
- 效果:
a∧b={TF当 a=T,b=T其他情况
- 示例:
>>> AND (T) (F) ("真") ("假")
"假"
- λ抽象表示:
λa.λb.a T b
- Python实现:
OR = lambda a: lambda b: a (T) (b)
- 效果:
a∨b={TF当 a=T 或 b=T其他情况
- 示例:
>>> OR (F) (T) ("真") ("假")
"真"
- λ抽象表示:
λa.λb.a (NOT b) T
- Python实现:
NAND = lambda a: lambda b: a (NOT (b)) (T)
- 效果:
¬(a∧b)
- 示例:
>>> NAND (T) (T) ("真") ("假")
"假"
- λ抽象表示:
λa.λb.a F (NOT b)
- Python实现:
NOR = lambda a: lambda b: a (F) (NOT (b))
- 效果:
¬(a∨b)
- 示例:
>>> NOR (F) (F) ("真") ("假")
"真"
- λ抽象表示:
λa.λb.a (NOT b) b
- Python实现:
XOR = lambda a: lambda b: a (NOT (b)) (b)
- 效果:
a⊕b={TF当 a=b其他情况
- 示例:
>>> XOR (T) (F) ("真") ("假")
"真"
- 所有运算均为柯里化形式:
AND (T) (F)
而非 AND(T, F)
- Church布尔值作为选择器工作:
T a b→aF a b→b
- 基础定义:
T = lambda a: lambda b: a # Church真值
F = lambda a: lambda b: b # Church假值