介绍 Church 编码和 Scott 编码。
邱奇数使用 lambda 构成的高阶函数来描述自然数。事实上邱奇编码可以用来描述一些很基本的结构,例如布尔值、元组、列表和 tagged unions。
可以将 0 表示为函数 zero 即 \f x. x。x 是什么并不重要,但我们可以将 f 令为 add1,将 x 令为 0。那么 0 就是 zero(add1, 0) = 0。
然后,可以将 1 表示为函数 one 即 \f x. f x。进行代换可以得到 one(add1, 0) = add1(0)。同理,将 2 表示为 \f x. f (f x)。
任意一个数 n 可以表示为($) f^n x,我们要想出一个结构实现把 f 执行 n 次,那实际上需要套一个递归的概念。下面来定义这个 Successor 函数 s。
递推函数 s 可以求出 n 的 Successor 为 \n f x -> f (($) n f x)。检查类型
1 | ghci> :t s |
可以理解为 s 是接受一个函数 n,返回另一个和 f/x 有关的函数。不妨简单带入
add1 (($) zero add1 0)为 1add1 (($) one add1 0)为 2
实现加法函数plus(m, n) = m + n,plus = \m n f x. m f(n f x)。这里用到了性质f^(m+n) x = f^m f^n x。
实现乘法函数multi(m, n) = m * n,multi = \m n f x. m (n f) x。相当于将 n f 应用 m 次在 x 上。而 n f 表示 n。
1 | add1 x = x + 1 |
1 | ghci> :t zero |
下面定义和 bool 量有关的函数。可以看到,true 就是传两个元素选择第一个,false 就是选择第二个。
1 | true x y = x |
容易看出,通过 true 和 false,可以自然而然定义出 if-then-else 语义
1 | ifte pred x y = ($) pred x y |
下面定义 pair 结构。不同于一般编程中指定如何构造结构,这里的思路是定义如何去消费这个结构。这里可以传入一个 sel。sel 可以是 fst 和 snd,表示选出 a 或者 b。
1 | pair a b sel = ($) sel a b |
这里有个小问题,写成 pair a b sel = sel $ a b 会有错误
1 | <interactive>:28:1: error: |
下面定义 pair 上的函数 next。(next (: pair a b)) 返回 (: pair (s a) a),可以理解为 pair 上的 Successor。
思路很简单,新构造一个 pair,它的第二个元素是 (fst p),第一个元素是 s (fst p)>
1 | next p = ($) pair (s (fst p)) (fst p) |
实现减法函数,首先先实现一个 pred 函数。它可以求出 n - 1 是什么。例如 pred two 是 one,但 pred zero 是 zero。这里的方案是从 pair zero zero 开始,调用 n 次 next,就可以得到 (n, n - 1)。使用 snd 返回就行。
1 | pred n = snd (($) n next (($) pair zero zero)) |
减法函数
1 | sub m n = ($) m (pred n) |
下面这个函数用来判断 n 是不是 0。
1 | isZero n = ($) n (\x -> false) true |
不妨进行代入来看看原理
zero
1
($) (\f x -> x) (\x -> false) true
这里的
f实际上就是(\x -> false),而 x 就是 true。所以肯定是 true。one
1
($) (\f x -> f x) (\x -> false) true
这里代入就是
(\x -> false) true,即 false。