方案中的Y组合器,使用惰性评估吗?

时间:2019-05-04 02:37:53

标签: lambda scheme y-combinator

有人知道如何在Scheme中实现Y组合器,特别是在使用惰性求值和附加参数的情况下吗?我的理解是Scheme(promise?)(delay)和(force)提供了懒惰的评估支持。

据我了解,带有应用程序的Y组合器如下所示,但是由于默认情况下会根据应用顺序进行评估,因此无法在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组合器如下:

;; 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

感谢您的指导!

2 个答案:

答案 0 :(得分:1)

是的,通过严格的评估,自我应用(x x)导致发生无限循环。如果您延迟此操作,则可以使用y-combinator,只要您还在该对象的使用站点上“强制”该延迟对象:

(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扩展,这是延迟自我施加的另一种方法,但不需要明确施加力。相反,强制由功能应用程序完成。

答案 1 :(得分:1)

常规Y组合器,这里是在懒惰球拍中计算(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

方案不是像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允许我们命名递归过程,但是在最后一个过程中,我们得到了一个承诺,即我们需要解决该承诺,因此我们泄漏了实现细节,因此使用起来更加困难。通常,在涉及诺言时,我们希望避免不必要的执行,但随后调用应返回诺言。