有人知道如何在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
感谢您的指导!
答案 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
使用delay
和force
并不能使它变得更好:
#!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
通常,Y
和Z
允许我们命名递归过程,但是在最后一个过程中,我们得到了一个承诺,即我们需要解决该承诺,因此我们泄漏了实现细节,因此使用起来更加困难。通常,在涉及诺言时,我们希望避免不必要的执行,但随后调用应返回诺言。