方案中的 Y 组合器使用教堂数字爆炸,但适用于常规数字

问题描述

我刚刚阅读了 Raul Rojas 的“Lambda 微积分教程介绍1”。我将 lambda 表达式放入 Scheme (TinyScheme) 中进行尝试。除了递归函数使用内存不足的 Y 组合器计算教堂数字 0,1,...,N 的总和外,一切都有效。奇怪的是,如果我使用常规数字计算总和,Y 组合器就可以工作。

这是我的 Y 组合器,

(define myY
  (lambda (y) 
    ((lambda (x) (x x)) 
      (lambda (x) 
        (y (lambda (a) ((x x) a)))))))

这里是一个递归函数,用于得到N个正则数之和,

(define sum1
  (lambda (r)
    (lambda (x)
      (cond
        ((zero? x) 0)
        (else (+ x (r (sub1 x))))))))

这有效

> ((myY sum1) 10)
55

在教程第 15 页的底部,递归函数 R=Lrx.Zx0(NS(r(PN))) 用于计算 N 个数字的总和(L=lambda)。在这个表达式中,r 将是递归函数,x 将是一个教堂数,Z 是零函数的测试,0 是一个作为教堂数的零,N 是一个教堂数,S 是后继函数,P 是前任函数。我已将其翻译成 Scheme 为,

(define myR
  (lambda (r)
    (lambda (x)
      (((myZ x) my0) ((x myS) (r (myP x))))
     )))

然而,即使取 0 数的总和也会爆炸。

> ((((myY myR) my0) add1) 0)
No memory!

在上面的行中,((myY myR) my0) 应该返回教会编号零。我只是让教会编号对 add1 采取行动,以获得人类可读的教会编号。

最初,我认为 Y 组合器是问题所在,但是,正如我所展示的,这适用于使用常规数字的递归函数。 Church 数的定义与适用于算术(加法、乘法、不等式)的 lambda 表达式的教程和 Scheme 版本相同。

编辑: 函数 myZ 是 lambda 表达式 Z=Lx.xF¬F,其中 F=Lxy.y 是 False(True 是 T=Lxy.x),而 ¬=Lx.xFT 不是。函数 Z 执行条件测试,因为 Z0=T 和 ZN=F,其中 0 是零的教堂数 (0=Lxy.y),N 是非零的教堂数 (1=Lxy.xy,2=Lxy。 x(xy) 等)实现 Z 的 Scheme 代码 myZ 是,

(define myZ
  (lambda (x)
    (((x myF) myNeg) myF)
    ))

解决方法

lambda 演算只适用于正常的降阶(即参数仅在需要它们的值时才计算),并且 Scheme 使用应用降阶(首先计算参数),“特殊形式”除外。

您的代码可以处理常规数字,不是因为数字,而是因为 cond,它最多会评估其中的一个子句。

如果您用常规函数替换 cond 中的 sum1,您的计算也不会以常规数字终止。

; This does not work
(define (conditional b t e)
  (if b t e))

(define sum1
  (lambda (r)
    (lambda (x)
      (conditional
        (zero? x)
        0
        (+ x (r (sub1 x)))))))

您可以通过添加一个间接级别来解决此问题;通过将它们包装在函数中来暂停对分支的评估:

(define sum1
  (lambda (r)
    (lambda (x)
      ((conditional
        (zero? x)
        (lambda (y) 0)
        (lambda (y) (+ x (r (sub1 x)))))
       "some argument"))))

相应的转换将在您的实现中起作用。