算术与教会数字

时间:2010-10-12 07:09:01

标签: scheme sicp lambda-calculus church-encoding

我正在通过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曾经说过的那样,“如果我不能建造它,我就无法理解......”

3 个答案:

答案 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 fnf申请已应用于x申请plus

  • 最后,留下一些的乐趣 - 这是定义(define plus (lambda (m) (lambda (n) ((m add1) n)))) 的另一种方式:

    {{1}}

    (好吧,不是太有趣,因为这个可能更明显。)

答案 1 :(得分:3)

(确保您理解higher-order functionsAlonzo Church' s untyped lambda calculus中,功能是唯一的原始数据类型。没有数字,布尔,列表或其他任何东西,只有函数。函数只能有1个参数,但函数可以接受和/或返回函数 - 不是这些函数的值,而是函数本身。因此,要表示数字,布尔值,列表和其他类型的数据,您必须为匿名函数提供一种聪明的方式来代表它们。 Church numerals是表示natural numbers的方式。无类型lambda演算中的三个最原始的构造是:

  1. λx.xidentity function,接受某些功能并立即将其返回。
  2. λx.x x,自我申请。
  3. λf.λx.f x,函数应用程序,接受函数和参数,并将函数应用于参数。
  4. 如何将0,1,2编码为除功能之外的其他内容?我们不知何故需要将数量的概念构建到系统中。我们只有函数,每个函数只能应用于1个参数。我们在哪里可以看到类似数量的东西?嘿,我们可以多次将函数应用于参数!在函数的3次重复调用中,显然存在数量感:f (f (f x))。所以让我们用lambda演算编码:

    • 0 = λf.λx.x
    • 1 = λf.λx.f x
    • 2 = λf.λx.f (f x)
    • 3 = λ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 badd1的原因:您将λ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个数字ab一起添加,两者都表示为教会数字,您希望替换 { 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))))