简介
既然 λ \lambda λ
演算与图灵机等价,那么理论上图灵机能干的事情都能通过 λ \lambda λ
演算表示。
考虑一个不支持任何除了 λ \lambda λ
表达式的编程语言…… 假设我们要在其中建立一个支持计数,并且支持各种运算的表示系统,该如何只用
λ \lambda λ
表达式来构造它?更进一步,是否可以仅用 λ \lambda λ
表达式来构造诸如链表的数据结构?
Church 编码便能解决这一问题,其可以定义与布尔数等价的 Church
Boolean,借助皮亚诺公理 (Peano Axioms) 能够定义出与自然数等价的 Church
Numeral,甚至还可以定义出 Church Pairs 和 Church
List…… 本文作为自己在学习 Church 编码所记录的笔记,可能有很多不合适的地方,等到以后自己有更加深入的理解后再来修改吧……
本文会随自己学习精度慢慢(咕咕)更新……
Church Booleans
定义
首先来考虑构造真和假。既然有两样东西,考虑选择二元函数:对于真所代表的函数,它总是返回第一个参数;而对于否所代表的函数,它总是返回第二个参数。于是:
t r u e = λ x . λ y . x f a l s e = λ x . λ y . y
\begin{aligned}
true & = \lambda x \ . \ \lambda y \ . \ x \\
false & = \lambda x \ . \ \lambda y \ . \ y
\end{aligned}
t r u e f a l se = λ x . λ y . x = λ x . λ y . y
发现这样构造带来一个非常有趣的性质:Church Boolean 似乎内置了
if - then - else \text{if - then - else} if - then - else
这种表达形式。考虑如下 λ \lambda λ
表达式:
b T F
b \enspace T \enspace F
b T F
其中 b b b
是一个 Church Boolean。发现其实际含义就是,若 b b b
为真,则结果为
T T T
,否则结果为 F F F
,跟 if b then T else F \text{if } \ b \ \text{ then } \ T \ \text{ else } \ F if b then T else F
实际上没有什么区别。所以实际上可以定义自己的条件分支表达式:
i f f = λ b . λ t . λ f . b t f
iff = \lambda b \ . \ \lambda t \ . \ \lambda f \ . \ b \enspace t \enspace f
i ff = λb . λ t . λ f . b t f
运算
可以应用上面提到的性质比较轻松地定义出所有的布尔逻辑运算……
非运算
先来看最简单的非运算。非运算是一个单参数的函数,对于 Church Boolean b b b
可以表示成
if b then t r u e else f a l s e \text{ if } \ b \ \text{ then } \ true \ \text{else} \ false if b then t r u e else f a l se
,因此可以写作:
n o t = λ b . f a l s e t r u e
not = \lambda b \ . \ false \enspace true
n o t = λb . f a l se t r u e
与运算
与运算则是一个双参数的函数,当且仅当两参数 a , b a, b a , b
都为 t r u e true t r u e
是才会返回
t r u e true t r u e
,否则返回 f a l s e false f a l se
。可以表示成
if a then ( if b then t r u e else f a l s e ) else f a l s e \text{ if } \ a \ \text{ then } ( \ \text{ if } \ b \ \text{ then } \ true \ \text{ else } \ false \ ) \ \text { else } \ false if a then ( if b then t r u e else f a l se ) else f a l se
,因此:
a n d = λ a . λ b . a ( b t r u e f a l s e ) f a l s e
and = \lambda a \ . \ \lambda b \ . \ a \enspace (b \enspace true \enspace false) \enspace false
an d = λa . λb . a ( b t r u e f a l se ) f a l se
或运算
或运算也是一个双参数的函数,只要两参数 a , b a, b a , b
中存在一个 t r u e true t r u e
即返回
t r u e true t r u e
,否则返回 f a l s e false f a l se
。可以表示成
if a then t r u e else ( if b then t r u e else f a l s e ) \text{ if } \ a \ \text{ then } \ true \ \text{else} \ ( \ \text{ if } \ b \ \text{ then } \ true \ \text{ else } \ false \ ) if a then t r u e else ( if b then t r u e else f a l se )
,因此:
o r = λ a . λ b . a t r u e ( b t r u e f a l s e )
or = \lambda a \ . \ \lambda b \ . \ a \enspace true \enspace (b \enspace true \enspace false)
or = λa . λb . a t r u e ( b t r u e f a l se )
其他?
诸如异或、同或等运算已经可以由上述三种运算组合起来表示了。
实现
用 Haskell 实现(
CodeWars: Church Booleans ):
{-# Language RankNTypes #-}
import Prelude hiding (Bool , False , True , not, and, or, (&&), (||), (==), (/=))
type Boolean = forall a. a -> a -> a
false, true :: Boolean
false = \ t f -> f
true = \ t f -> t
not :: Boolean -> Boolean
and , or , xor :: Boolean -> Boolean -> Boolean
not = \ a -> a false true
and = \ a b -> a (b true false) false
or = \ a b -> a true (b true false)
Church Maybe
Church Numerals
Peano Axioms
在讨论 Church 数前,先来看看自然数是怎么被定义的。显然可以递归地定义自然数(类似于数学归纳法),首先定义
0 0 0
,其次对于任意自然数 n n n
定义一个能获得其后继元素的函数
S ( n ) S(n) S ( n )
(对于自然数而言,显然 S ( n ) = n + 1 S(n) = n + 1 S ( n ) = n + 1
)就行了。严谨地说,定义自然数集
N \mathbb{N} N
的过程如下:
0 ∈ N 0 \in \mathbb{N} 0 ∈ N
定义等价关系 (equality relation):
自反性 :∀ x ∈ N , x = x \forall x \in \mathbb{N},\ x = x ∀ x ∈ N , x = x
对称性 :∀ x , y ∈ N , x = y ⇒ y = x \forall x, y \in \mathbb{N},\ x = y \Rightarrow y = x ∀ x , y ∈ N , x = y ⇒ y = x
传递性 :∀ x , y , z ∈ N , x = y , y = z ⇒ x = z \forall x, y, z \in \mathbb{N},\ x = y,\ y = z \Rightarrow x = z ∀ x , y , z ∈ N , x = y , y = z ⇒ x = z
封闭性 :∀ x ∈ N , x = y ⇒ y ∈ N \forall x \in \mathbb{N},\ x = y \Rightarrow y \in \mathbb{N} ∀ x ∈ N , x = y ⇒ y ∈ N
定义算术性质 (arithmetical properties):
∀ n ∈ N , S ( n ) ∈ N \forall n \in \mathbb{N},\ S(n) \in \mathbb{N} ∀ n ∈ N , S ( n ) ∈ N
∀ m , n ∈ N , m = n ⇔ S ( m ) = S ( n ) \forall m, n \in \mathbb{N},\ m = n \Leftrightarrow S(m) = S(n) ∀ m , n ∈ N , m = n ⇔ S ( m ) = S ( n )
∀ n ∈ N , S ( n ) ≠ 0 \forall n \in \mathbb{N},\ S(n) \neq 0 ∀ n ∈ N , S ( n ) = 0
归纳公理 (induction axiom):
若 K \mathbb{K} K
是一个集合,满足:
0 ∈ K 0 \in \mathbb{K} 0 ∈ K
∀ n ∈ K , S ( n ) ∈ K \forall n \in \mathbb{K},\ S(n) \in \mathbb{K} ∀ n ∈ K , S ( n ) ∈ K
则:N ⊆ K \mathbb{N} \subseteq \mathbb{K} N ⊆ K
定义
既然只要有了后继函数和 0 0 0
就可以表示出所有的自然数,那么依然考虑用二元函数来表述 Church 数,形如:
λ f . λ x . s o m e t h i n g
\lambda f \ . \ \lambda x \ . \ something
λ f . λ x . so m e t hin g
其中第一个参数 f f f
可以看作后继函数,第二个参数 x x x
可看作 0 0 0
。由此可以很方便地使用这个式子定义 0 0 0
:
C 0 = λ f . λ x . x
C_0 = \lambda f \ . \ \lambda x \ . \ x
C 0 = λ f . λ x . x
可以看到,不论我们传递给它什么后继函数,它所返回的结果都是 0 0 0
。进一步,也可以比较轻松地定义:
C 1 = λ f . λ x . f x C 2 = λ f . λ x . f ( f x ) C 3 = λ f . λ x . f ( f ( f x ) ) …
\begin{aligned}
C_1 & = \lambda f \ . \ \lambda x \ . \ f \enspace x \\
C_2 & = \lambda f \ . \ \lambda x \ . \ f \enspace (f \enspace x) \\
C_3 & = \lambda f \ . \ \lambda x \ . \ f \enspace (f \enspace (f \enspace x)) \\
& \dots
\end{aligned}
C 1 C 2 C 3 = λ f . λ x . f x = λ f . λ x . f ( f x ) = λ f . λ x . f ( f ( f x )) …
可见,我们可以通过 Church Numeral 得到与自然数等价的任何事物。例如对于自然数
n n n
,有:
n = C n ( + 1 ) 0
n = C_n \enspace (+1) \enspace 0
n = C n ( + 1 ) 0
基础运算
后继元素
在尝试解决加法前我们先解决一个看起来简单一些的问题:以 C n C_n C n
为参数,而以
C n + 1 C_{n + 1} C n + 1
为返回值的函数 s u c c succ s u cc
应当如何定义?
如果我们能够定义出 s u c c succ s u cc
,那么将 s u c c succ s u cc
作用 b b b
次于 C a C_a C a
即可达到
C a + C b C_a + C_b C a + C b
的效果,更进一步,如果定义出了加法,则将 ( + C a ) (+ C_a) ( + C a )
作用 b b b
次于 0 0 0
即可达到 C a ⋅ C b C_a \cdot C_b C a ⋅ C b
的效果。
为了方便理解,先继续以自然数 n n n
作为例子:
n + 1 = C n + 1 ( + 1 ) 0 = C n ( + 1 ) 1 = C n ( + 1 ) ( C 1 ( + 1 ) 0 )
\begin{aligned}
n + 1
& = C_{n + 1} \enspace (+1) \enspace 0 \\
& = C_n \enspace (+1) \enspace 1 \\
& = C_n \enspace (+1) \enspace (C_1 \enspace (+1) \enspace 0) \\
\end{aligned}
n + 1 = C n + 1 ( + 1 ) 0 = C n ( + 1 ) 1 = C n ( + 1 ) ( C 1 ( + 1 ) 0 )
这启示我们,当 s u c c succ s u cc
作用于 C n C_n C n
时,只需要保持后继函数 f f f
不变,同时将零元由
x x x
变为 f x f \enspace x f x
就可以了。因此:
s u c c = λ M . λ f . λ x . f ( M f x )
succ = \lambda M \ . \ \lambda f \ . \ \lambda x \ . \ f \enspace (M \enspace f \enspace x)
s u cc = λ M . λ f . λ x . f ( M f x )
举个例子,试试用它作用于 C 0 C_0 C 0
:
s u c c C 0 = ( λ M . λ f . λ x . f ( M f x ) ) λ f . λ x . x = ( λ f . λ x . f ( M f x ) ) [ M : = λ f . λ x . x ] = ( λ g . λ y . g ( M g y ) ) [ M : = λ f . λ x . x ] = ( λ g . λ y . g ( ( λ f . λ x . x ) g y ) ) = λ g . λ y . g y = λ f . λ x . f x = C 1
\begin{aligned}
succ \enspace C_0
& = (\lambda M \ . \ \lambda f \ . \ \lambda x \ . \ f \enspace (M \enspace f \enspace x)) \enspace \lambda f \ . \ \lambda x \ . \ x \\
& = (\lambda f \ . \ \lambda x \ . \ f \enspace (M \enspace f \enspace x)) \enspace [M := \lambda f \ . \ \lambda x \ . \ x] \\
& = (\lambda g \ . \ \lambda y \ . \ g \enspace (M \enspace g \enspace y)) \enspace [M := \lambda f \ . \ \lambda x \ . \ x] \\
& = (\lambda g \ . \ \lambda y \ . \ g \enspace ((\lambda f \ . \ \lambda x \ . \ x) \enspace g \enspace y)) \\
& = \lambda g \ . \ \lambda y \ . \ g \enspace y \\
& = \lambda f \ . \ \lambda x \ . \ f \enspace x \\
& = C_1
\end{aligned}
s u cc C 0 = ( λ M . λ f . λ x . f ( M f x )) λ f . λ x . x = ( λ f . λ x . f ( M f x )) [ M := λ f . λ x . x ] = ( λ g . λ y . g ( M g y )) [ M := λ f . λ x . x ] = ( λ g . λ y . g (( λ f . λ x . x ) g y )) = λ g . λ y . g y = λ f . λ x . f x = C 1
加法
前面成功定义出了 s u c c succ s u cc
,但还留下了一个问题:怎么表示
s u c c ∘ s u c c ∘ ⋯ ∘ s u c c ⏟ b times \underbrace{succ \circ succ \circ \dots \circ succ}_{b \text{ times}} b times s u cc ∘ s u cc ∘ ⋯ ∘ s u cc
(下面简写为
s u c c b succ^b s u c c b
)?
s u c c b = [ λ M . λ f . λ x . f ( M f x ) ] b = λ x . f b x [ f : = λ M . λ f . λ x . f ( M f x ) ] = ( λ f . λ x . f b x ) λ M . λ f . λ x . f ( M f x ) = C b s u c c
\begin{aligned}
succ^b
& = [ \lambda M \ . \ \lambda f \ . \ \lambda x \ . \ f \enspace (M \enspace f \enspace x) ]^b \\
& = \lambda x \ . \ f^b \enspace x \enspace [ f := \lambda M \ . \ \lambda f \ . \ \lambda x \ . \ f \enspace (M \enspace f \enspace x) ] \\
& = (\lambda f \ . \ \lambda x \ . \ f^b \enspace x) \enspace \lambda M \ . \ \lambda f \ . \ \lambda x \ . \ f \enspace (M \enspace f \enspace x) \\
& = C_b \enspace succ
\end{aligned}
s u c c b = [ λ M . λ f . λ x . f ( M f x ) ] b = λ x . f b x [ f := λ M . λ f . λ x . f ( M f x )] = ( λ f . λ x . f b x ) λ M . λ f . λ x . f ( M f x ) = C b s u cc
简要推广,对于任意一元 Church Numeral 函数 f f f
,都有:
f b = C b f
f^b = C_b \enspace f
f b = C b f
于是对于加法:
C a + C b = ( + C b ) C a = ( C b s u c c ) C a
\begin{aligned}
C_a + C_b
& = (+ C_b) \enspace C_a \\
& = (C_b \enspace succ) \enspace C_a
\end{aligned}
C a + C b = ( + C b ) C a = ( C b s u cc ) C a
我们也可以不考虑 s u c c succ s u cc
,换一个角度考虑加法:其可看作二元函数,输入 C a , C b C_a, C_b C a , C b
并且返回 C a + b C_{a + b} C a + b
。依然以自然数 a , b a, b a , b
举例:
a + b = C a + b ( + 1 ) 0 = C a ( + 1 ) b = C a ( + 1 ) ( C b ( + 1 ) 0 )
\begin{aligned}
a + b
& = C_{a + b} \enspace (+1) \enspace 0 \\
& = C_a \enspace (+1) \enspace b \\
& = C_a \enspace (+1) \enspace (C_b \enspace (+1) \enspace 0)
\end{aligned}
a + b = C a + b ( + 1 ) 0 = C a ( + 1 ) b = C a ( + 1 ) ( C b ( + 1 ) 0 )
故将 C a C_a C a
作用于后继函数 f f f
,同时将 C b C_b C b
作用于零元 x x x
即可得到
C a + b C_{a + b} C a + b
:
( + ) = λ M . λ N . λ f . λ x . M f ( N f x )
(+) = \lambda M \ . \ \lambda N \ . \ \lambda f \ . \ \lambda x \ . \ M \enspace f \enspace (N \enspace f \enspace x)
( + ) = λ M . λ N . λ f . λ x . M f ( N f x )
乘法
与之前类似地,可以借助加法表示乘法,将 ( + C a ) (+ C_a) ( + C a )
作用 b b b
次于 C 0 C_0 C 0
即可:
C a ⋅ C b = C b ( + C a ) C 0
C_a \cdot C_b = C_b \enspace (+ C_a) \enspace C_0
C a ⋅ C b = C b ( + C a ) C 0
若不借助加法,可考虑:乘法运算以 C a , C b C_a, C_b C a , C b
为参数,并返回
C a ⋅ b C_{a \cdot b} C a ⋅ b
。依然以自然数 a , b a, b a , b
举例:
a ⋅ b = C a ⋅ b ( + 1 ) 0 = C a ( + b ) 0 = C a ( C b ( + 1 ) ) 0
\begin{aligned}
a \cdot b
& = C_{a \cdot b} \enspace (+1) \enspace 0 \\
& = C_a \enspace (+b) \enspace 0 \\
& = C_a \enspace (C_b \enspace (+1)) \enspace 0
\end{aligned}
a ⋅ b = C a ⋅ b ( + 1 ) 0 = C a ( + b ) 0 = C a ( C b ( + 1 )) 0
这启示我们,只需要将 C a , C b C_a, C_b C a , C b
依次作用于后继函数 f f f
,并且保持零元 x x x
不变即可:
( ⋅ ) = λ M . λ N . λ f . λ x . M ( N f ) x
(\cdot) = \lambda M \ . \ \lambda N \ . \ \lambda f \ . \ \lambda x \ . \ M \enspace (N \enspace f) \enspace x
( ⋅ ) = λ M . λ N . λ f . λ x . M ( N f ) x
幂运算
与自然数的情况类似,幂运算还是得借助乘法实现,将 ( ⋅ C a ) (\cdot C_a) ( ⋅ C a )
作用 b b b
次于
C 1 C_1 C 1
即可:
C a ^ C b = C b ( ⋅ C a ) C 1
C_a \hat{\enspace} C_b = C_b \enspace (\cdot C_a) \enspace C_1
C a ^ C b = C b ( ⋅ C a ) C 1
实现
用 Haskell 实现(
CodeWars: Church Numbers ):
{-# LANGUAGE RankNTypes #-}
import Prelude hiding (succ)
newtype Number = Nr (forall a. (a -> a) -> a -> a)
zero :: Number
zero = Nr (\ _ x -> x)
succ :: Number -> Number
succ (Nr a) = Nr (\ f x -> f (a f x))
one :: Number
one = succ zero
add :: Number -> Number -> Number
add (Nr a) = a succ
-- add (Nr a) (Nr b) = Nr (\ f x -> a f (b f x))
mult :: Number -> Number -> Number
mult (Nr a) b = a (add b) zero
-- mult (Nr a) (Nr b) = Nr (\ f x -> a (b f) x)
pow :: Number -> Number -> Number
pow x (Nr n) = n (mult x) one
减法?
对于 Church
Numeral 的减法是一个非常有趣且不简单的问题。先考虑较简单的情况:对于以 C n C_n C n
为参数,C n − 1 C_{n - 1} C n − 1
为结果的函数 p r e d pred p re d
应当如何定义(即求前驱元素)。
很快发现一个问题:p r e d C 0 = ? pred \enspace C_0 = ? p re d C 0 = ?
为了保证 p r e d pred p re d
是 well-defined 的,则:
p r e d C n = { C 0 n = 0 C n − 1 otherwise
pred \enspace C_n =
\begin{cases}
C_0 & n = 0 \\
C_{n - 1} & \text{otherwise}
\end{cases}
p re d C n = { C 0 C n − 1 n = 0 otherwise
Refs