4.5 数值编码
在Lambda演算中,又很多给数值编码的方法,但是最流行的方法来自于Church,因此数值编码又被称为 Church 数。它的思想是一个自然数 n 被编码成一个有两个参数的函数,参数分别为 f 与 x,函数将f应用于x n遍。因此,0的函数接收一个 f 和 x,返回 x(相当于应用 f 0次)。1 的函数应用 f 一次,以此类推。
方法 add1 需要接收数值 n 的代表,并且生产出数值 n + 1的代表。换言之,它接收一个含有两个参数的函数并且返回另一个含有两个参数的函数;新的函数将应用它第一个参数于第二个参数 n + 1 次。为了使第一个 n 被应用,新函数应该使用旧的 n。
类似于 true 和 false 的编码,数值编码也很简便。为了对两个数值 n 和 m 使用加法,我们只需应用 add1 到 n 共 m 次 —— 而 m 恰巧是一个会接收 add1 并应用其 m 次的函数。
将数值作为函数的想法对定义 iszero 也十分有用,该函数接收数值后,如果数值为 0 则返回 true,其他返回 false;如果这个函数应用 0 次于 true,则结果为 true,其他情况则为 false。
为了将函数推广到数相等,我们需要减法。就像我们用 add1 实现加法一样,减法可以用 sub1 实现。但是,尽管 add1,add 和 iszero 函数在 Church 的书编码中非常简单,sub1 会显得略微复杂。sub1 将接收的数值函数作为参数,并且应用 n 次,但是其返回的函数却要 少 应用函数一次。当然,任意函数的逆函数不可用于反转一个应用程序。
实现 sub1 的函数分为两个部分:
- 将给定的参数 x 与 true 成对。 true 标示了对 f 的应用应该被跳过
- 将给定的函数 f 封包,以接收对值,以及当对值用含有 false 时应用 f 。必定返回一个包含 false 的对值,以确保 f 会在将来被应用。
wrap 函数将给定的 f 封包:
sub1 函数接收一个 n 并且返回一个新函数,新函数接收 f 和 x, 用 wrap 函数将 f 封包,并且将 x 与 true 对编码, 在 (wrap f) 和 ⟨true, x⟩ 上使用 n,并且抽出结果的第二部分 —— f 应用于 x (n - 1) 次。
有关编码的提示:对 0 的编码与对 false 的编码一致。因此,没有程序能够区分 0 与 false,编程者必须确保只有在布尔上下文中才使用 true 和 false。这与 C语言使用相同的比特值实现 0、false和null指针类似。
练习 4.6
证明 add1 1 =n 2
题解
add1 1 = (λn.λf.λx.f (n f x)) (λf.λx.f x)
→nβ λf.λx.f ((λf.λx.f x) f x)
→nβ λf.λx.f ((λx.f x) x)
→nβ λf.λx.f ((λx.f x) x)
→nβ λf.λx.f (f x) = 2
练习 4.7
证明 iszero 1 =n false
题解
iszero 1 = λn.n (λx.false) true (λf.λx.f x)
→nβ (λf.λx.f x) (λx.false) true
→nβ (λx.(λx.false) x) true
→nβ (λx.false) true
→nβ false
练习 4.8
证明 sub1 1 =n 0
题解
sub1 1 = (λn.λf.λx.snd (n (wrap f) ⟨true, x⟩)) (λf.λx.f x)
→nβ λf.λx.snd ((λf.λx.f x) (wrap f) ⟨true, x⟩)
→nβ λf.λx.snd (λx.(wrap f) x)⟨true, x⟩)
→nβ λf.λx.snd ((wrap f) ⟨true, x⟩)
= λf.λx.snd (((λf.λp.⟨false, if (fst p) (snd p) (f (snd p))⟩) f) ⟨true, x⟩)
→nβ λf.λx.snd ((λp.⟨false, if (fst p) (snd p) (f (snd p))⟩) ⟨true, x⟩)
→nβ λf.λx.snd (⟨false, if (fst ⟨true, x⟩) (snd ⟨true, x⟩) (f (snd ⟨true, x⟩))⟩)
→→n λf.λx.snd (⟨false, if true (snd ⟨true, x⟩) (f (snd ⟨true, x⟩))⟩
→→n λf.λx.snd (⟨false, snd ⟨true, x⟩)⟩
→→n λf.λx.snd ⟨false, x⟩
→→n λf.λx.x
= 0
练习 4.9
用帮助我们实现 add 的方式定义 mult 。换句话说,实现 (mult n m) ,利用 n 本身应用函数 n 次的事实,应用 m 于 0 n 次 加法。(提示: (add m) 是什么类型的值?)
题解
mult ≐ λn.λm.λf.m (n f)
练习 4.10
Lambda演算没有提供错误信号的机制,如果 sub1应用于 0 之后,将会发生什么?当 iszero 应用于 true 后,将会发生什么?
题解
-
sub1 0 = (λn.λf.λx.snd (n (wrap f) ⟨true, x⟩)) (λf.λx.x)
→nβ λf.λx.snd ((λf.λx.x) (wrap f) ⟨true, x⟩)
→nβ λf.λx.snd ((λx.x) ⟨true, x⟩)
→nβ λf.λx.snd ⟨true, x⟩
→→n λf.λx.x = 0
-
iszero true = (λn.n (λx.false) true) (λx.λy.x)
→nβ (λx.λy.x) (λx.false) true
→nβ λy.λx.false true
→nβ λx.false