我正在通过SICP工作,problem 2.6让我陷入了困境。在处理教会数字时,将零和1编码为满足某些公理的任意函数的概念似乎是有意义的。另外,使用零的定义导出单个数字的直接公式,并且add-1函数是有意义的。我不明白如何形成一个加号运算符。
到目前为止,我有这个。
(define zero (lambda (f) (lambda (x) x)))
(define (add-1 n)
(lambda (f) (lambda (x) (f ((n f) x)))))
(define one (lambda (f) (lambda (x) (f x))))
(define two (lambda (f) (lambda (x) (f (f x)))))
通过lambda calculus的维基百科条目,我发现加的定义是PLUS:=λmnfx.mf(n f x)。使用该定义,我能够制定以下程序。
(define (plus n m)
(lambda (f) (lambda (x) ((m f) ((n f) x)))))
我不明白的是,如何仅使用先前派生的程序给出的信息直接导出该过程。任何人都可以用某种严格的证明形式回答这个问题吗?直觉上,我想我明白发生了什么,但正如Richard Feynman曾经说过的那样,“如果我不能建造它,我就无法理解......”
答案 0 :(得分:14)
实际上非常简单。这可能会被视为火焰,但是parens使得它更难以看到 - 更好的方式来看看发生了什么是想象你是一个讨厌的语言,或者只是使用Scheme具有多参数函数和拥抱那......这是一个解释,在方便的地方使用lambdas和多个参数:
每个数字N都编码为
(lambda (f x) ...apply (f (f (f ... (f x)))) N times...)
这意味着N的编码实际上是
(lambda (f x) (f^N x))
其中f^N
是功能取幂。
更简单的说法(假设是currying):数字N编码为
(lambda (f) f^N)
所以N实际上是“提升到N的力量”功能
现在接受你的表达(在这里查看lambda
):
((m f) ((n f) x))
因为n
是一个数字的编码,所以它是取幂,所以这实际上是:
((m f) (f^n x))
和m
相同:
(f^m (f^n x))
其余部分应该是显而易见的...... m
f
个n
个f
申请已应用于x
申请plus
。
最后,留下一些的乐趣 - 这是定义(define plus (lambda (m) (lambda (n) ((m add1) n))))
的另一种方式:
{{1}}
(好吧,不是太有趣,因为这个可能更明显。)
答案 1 :(得分:3)
(确保您理解higher-order functions)。在Alonzo Church' s untyped lambda calculus中,功能是唯一的原始数据类型。没有数字,布尔,列表或其他任何东西,只有函数。函数只能有1个参数,但函数可以接受和/或返回函数 - 不是这些函数的值,而是函数本身。因此,要表示数字,布尔值,列表和其他类型的数据,您必须为匿名函数提供一种聪明的方式来代表它们。 Church numerals是表示natural numbers的方式。无类型lambda演算中的三个最原始的构造是:
λx.x
,identity function,接受某些功能并立即将其返回。λx.x x
,自我申请。λf.λx.f x
,函数应用程序,接受函数和参数,并将函数应用于参数。如何将0,1,2编码为除功能之外的其他内容?我们不知何故需要将数量的概念构建到系统中。我们只有函数,每个函数只能应用于1个参数。我们在哪里可以看到类似数量的东西?嘿,我们可以多次将函数应用于参数!在函数的3次重复调用中,显然存在数量感:f (f (f x))
。所以让我们用lambda演算编码:
λf.λx.x
λf.λx.f x
λf.λx.f (f x)
λf.λx.f (f (f x))
等等。但你怎么从0到1,或从1到2呢?你怎么写一个函数,给定一个数字,会返回一个加1的数字?我们看到教会数字中的模式,该术语始终以λf.λx.
开头,并且在您有限地重复应用 f 之后,我们需要以某种方式进入{{1}的主体并将其包装到另一个λf.λx.
中。如何在不减少的情况下改变抽象体?好吧,你可以应用一个函数,将函数包装在一个函数中,然后将新函数包装到旧的lambda抽象中。但是你不想要改变参数,因此你将抽象应用于同名的值:f
,但是((λf.λx.f x) f) x → f x
,这不是我们需要的。
((λf.λx.f x) a) b) → a b
为add1
的原因:您将λn.λf.λx.f ((n f) x)
应用于n
然后f
将表达式减少到身体,然后将x
应用于该主体,然后使用f
再次对其进行抽象。 练习也看到它是真的,快速了解β-reduction并减少λf.λx.
将2递增2。
现在理解将主体包装到另一个函数调用背后的直觉,我们如何实现添加2个数字?我们需要一个函数,给定(λn.λf.λx.f ((n f) x)) (λf.λx.f (f x))
(2)和λf.λx.f (f x)
(3),将返回λf.λx.f (f (f x))
(5)。请看2.如果您可以将λf.λx.f (f (f (f (f x))))
替换为x
,身体为3,即f (f (f x))
怎么办?要获得3的身体,这很明显,只需将其应用于f
然后x
。现在将2应用于f
,然后将其应用于3的正文,而不是x
。然后再次将其包裹在λf.λx.
中:λa.λb.λf.λx.a f (b f x)
。
结论:要将2个数字a
和b
一起添加,两者都表示为教会数字,您希望替换 { x
a
的{{1}}正文{1}},b
+ f (f x)
= f (f (f x))
。为实现此目的,请将f (f (f (f (f x))))
应用于a
,然后应用f
。
答案 2 :(得分:2)
Eli的答案在技术上是正确的,但由于此问题被问及#apply
程序尚未引入,我不认为作者希望学生掌握相关知识那些或像currying这样的概念能够回答这个问题。
他们通过建议应用替换方法,然后从那里注意到加法的效果是一个数字组合到另一个上,它们几乎引导了答案。构图是在练习1.42中引入的概念;以及了解添加剂程序如何在该系统中起作用所需的全部内容。
; The effect of procedure #add-1 on `one`, and `two` was the composition of `f`
; onto `one` and `f` onto `two`.
;
; one : (λ (f) (λ (x) (f x)))
; two : (λ (f) (λ (x) (f (f x))))
; three : (λ (f) (λ (x) (f (f (f x)))))
;
; Thus one may surmise from this that an additive procedure in this system would
; work by composing one number onto the other.
;
; From exercise 1.42 you should already have a procedure called #compose.
;
; With a little trial and error (or just plain be able to see it) you get the
; following solution.
(define (adder n m)
(λ (f)
(let ((nf (n f))
(mf (m f)))
(compose nf mf))))