Scheme 教会数字算术

Scheme 教会数字算术,scheme,sicp,lambda-calculus,church-encoding,Scheme,Sicp,Lambda Calculus,Church Encoding,我正在通过SICP工作,这让我有些进退两难。在处理教会数字时,将零和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) (

我正在通过SICP工作,这让我有些进退两难。在处理教会数字时,将零和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)))))
通过查看维基百科条目,我发现plus的定义是plus:=λmnfx.mf(nfx)。根据这个定义,我可以制定以下程序

(define (plus n m)
  (lambda (f) (lambda (x) ((m f) ((n f) x)))))

我不明白的是,如何仅使用先前导出的过程提供的信息直接导出该过程。有人能以某种严格的证明形式回答这个问题吗?直觉上,我认为我理解正在发生的事情,但正如理查德·费曼(Richard Feynman)曾经说过的,“如果我不能构建它,我就无法理解它……”

其实很简单。这可能会被视为是一种诱饵,但帕伦人让人更难看到——更好的方法是想象你使用的是一种咖喱语,或者仅仅使用Scheme具有多参数函数的事实并接受它。。。下面是一个使用lambdas和多参数的解释:

  • 每个数字N被编码为

    (lambda (f x) ...apply (f (f (f ... (f x)))) N times...)
    
    (lambda (f) f^N)
    
  • 这意味着N的编码实际上是

    (lambda (f x) (f^N x))
    
    其中
    f^N
    是函数求幂

  • 一种更简单的方法(假设使用curry):数字N被编码为

    (lambda (f x) ...apply (f (f (f ... (f x)))) N times...)
    
    (lambda (f) f^N)
    
    所以N实际上是一个“提升到N的幂”函数

  • 现在使用您的表达式(查看此处的
    lambda
    s):

    因为
    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))))
    
    (嗯,没有太多乐趣,因为这一点可能更明显。)


Eli的回答在技术上是正确的,但由于在提出这个问题时没有引入
#apply
程序,我认为作者并不打算让学生了解这一点或咖喱等概念来回答这个问题

他们几乎是通过建议一个人应用替代方法来引导一个人找到答案,然后从那里我们应该注意到加法的效果是一个数字与另一个数字的组合。作文是练习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))))
(确保你理解)。在的中,函数是唯一的基元数据类型。没有数字、布尔、列表或其他任何东西,只有函数。函数只能有一个参数,但函数可以接受和/或返回函数,而不是这些函数的值,而是函数本身。因此,要表示数字、布尔值、列表和其他类型的数据,必须想出一种聪明的方法,让匿名函数代表它们。是代表的方式。非类型化lambda演算中最原始的三个构造是:

  • λx.x
    ,一个函数,接受某个函数并立即返回它
  • λx.x x
    ,自行应用
  • λf.λx.f x
    ,函数应用,获取函数和参数,并将函数应用于参数
  • 如何将0、1、2编码为函数?我们需要在系统中建立数量的概念。我们只有函数,每个函数只能应用于一个参数。我们在哪里可以看到类似数量的东西?嘿,我们可以对一个参数应用一个函数多次!一个函数的3次重复调用显然有一种数量感:
    f(f(fx))
    。让我们用lambda演算对其进行编码:

    • 0=
      λf.λx.x
    • 1=
      λf.λx.fx
    • 2=
      λf.λx.f(fx)
    • 3=
      λf.λx.f(f(f x))
    等等。但是如何从0到1,或从1到2?如果给定一个数字,您将如何编写一个返回递增1的数字的函数?我们看到教堂数字中的模式,即术语总是以
    λf.λx.
    开头,并且在有限次重复应用f之后,因此我们需要以某种方式进入
    λf.λx.
    的主体,并将其包装成另一个
    f
    。如何在不减少的情况下更改抽象体?那么,您可以应用一个函数,将主体包装在一个函数中,然后将新主体包装到旧的lambda抽象中。但是您不希望参数改变,因此您将抽象应用于同名的值:
    ((λf.λx.f x)f)x→ f x
    ,但
    ((λf.λx.f x)a)b)→ a b
    ,这不是我们需要的

    这就是为什么
    add1
    λn.λf.λx.f((nf)x)
    :将
    n
    应用于
    f
    ,然后
    x
    将表达式减少到主体,然后将
    f
    应用到该主体,然后用
    λf.λx.
    再次提取它练习:也要看到这是真的,快速学习并减少
    (λn.λf.λx.f((nf)x))(λf.λx.f(fx))
    以1增加2

    现在理解了将主体包装到另一个函数调用中的直觉,我们如何实现2个数字的加法?我们需要一个函数,给定
    λf.λx.f(fx)
    (2)和
    λf.λx.f(fx))
    (3),它将返回
    λf.λx.f(f(fx))
    (5)。看2。如果你能把它换掉呢<