Scheme 中的 Y 组合器,使用惰性求值?

Y-combinator in Scheme, using lazy evaluation?

有谁知道如何在 Scheme 中实现 Y 组合器,特别是惰性求值和附加参数?我的理解是 Scheme (promise?) (delay) 和 (force) 提供惰性求值支持。

据我了解,Y-combinator with application 如下所示,但由于默认情况下应用顺序评估,因此无法在 Scheme 中使用。

((lambda (f)
   ((lambda (x) (f (x x)))
    (lambda (x) (f (x x)))))
 (lambda (r) (r)))

这是一个建议的解决方案(Z 组合器),但是它使用另一个带参数的 lambda 函数作为解决方案:

;; Z-combinator
(define Z
  (lambda (f)
    ((lambda (g) (f (g g)))
     (lambda (g) (f (lambda args (apply (g g)
                                        args)))))))
;Value: z

((Z (lambda (r)
      (lambda (x)
        (if (< x 2)
            1
            (* x (r (- x 1))))))) 5)
;Value: 120

;; end Z-combinator

根据解决方案更新

Y-combinator如下:

;; Y-combinator
(define Y
  (lambda (f)
      ((lambda (x) (f (delay (x x))))
       (lambda (x) (f (delay (x x)))))))
;Value: y

;; end Y-combinator

;; Non tail recursive
((Y (lambda (r)
      (lambda (x)
        (if (< x 2)
            1
            (* x ((force r) (- x 1)))))))
   5)
;Value: 120

;; Tail - recursive
((Y (lambda (r)
      (lambda (x acc)
        (if (< x 2)
            acc
            ((force r) (- x 1) (* x acc))))))
   5 1)

;Value: 120

感谢您的指导!

是的,经过严格评估,自我应用程序 (x x) 会导致出现无限循环。如果你延迟这个,你可以使用 y 组合器,只要你也 'force' 在它的使用站点延迟对象:

(define (test)
  (((lambda (f)
      ((lambda (x) (f (delay (x x))))
       (lambda (x) (f (delay (x x))))))
    (lambda (r)
      (lambda (x)
        (if (< x 2)
            1
            (* x ((force r) (- x 1)))))))
   5))

制作在严格设置下工作的 y 组合器变体的正常方法是 eta 扩展自应用程序,这是另一种延迟它但不需要明确应用力的方法。相反,强制是由函数应用程序完成的。

普通顺序Y组合器,这里在Lazy Racket中计算(ack 3 6)

#lang lazy

(define Y
  (lambda (f)
    ((lambda (g) (g g))
     (lambda (g) (f (g g))))))

((Y (lambda (ackermann)
      (lambda (m n)
        (cond
        ((= m 0) (+ n 1))
        ((= n 0) (ackermann (- m 1) 1))
        (else (ackermann (- m 1) (ackermann m (- n 1))))))))
 3
 6) ; ==> 509

Scheme 不是像 Lazy Racket 这样的惰性语言,因此 Y 需要稍有不同。它现在称为 Z 组合器:

#!r6rs
(import (rnrs base))

(define Z
  (lambda (f)
    ((lambda (g)
       (f (g g)))
     (lambda (g)
       (f (lambda args (apply (g g) args)))))))

((Z (lambda (ackermann)
      (lambda (m n)
        (cond
        ((= m 0) (+ n 1))
        ((= n 0) (ackermann (- m 1) 1))
        (else (ackermann (- m 1) (ackermann m (- n 1))))))))
 3
 6) ; ==> 509

使用 delayforce 并没有真正使它变得更好:

#!r6rs
(import (rnrs base)
        (rnrs r5rs))

(define DY
  (lambda (f)
    ((lambda (g) (g g))
     (lambda (g) (f (delay (g g)))))))


((DY (lambda (d-ackermann)
      (lambda (m n)
        (cond
          ((= m 0) (+ n 1))
          ((= n 0) ((force d-ackermann) (- m 1) 1))
          (else ((force d-ackermann) (- m 1) ((force d-ackermann) m (- n 1))))))))
 3
 6) ; ==> 509

通常 YZ 允许我们命名我们的递归过程,但在最后一个中,我们得到了一个我们需要解析的承诺,因此我们泄露了实现细节并且变得更加困难使用。通常当涉及承诺时,我们希望避免不必要的执行,但调用应该 return 承诺。