【问题标题】:Y-combinator in Scheme, using lazy evaluation?Scheme中的Y-combinator,使用惰性求值?
【发布时间】:2019-09-22 13:03:04
【问题描述】:

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

据我了解,Y-combinator 与 application 如下,但由于应用顺序评估,默认情况下在 Scheme 中不起作用。

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

这是一个建议的解决方案(Z-combinator),但是它使用另一个带有参数的 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

感谢您的指导!

【问题讨论】:

    标签: lambda scheme y-combinator


    【解决方案1】:

    正常顺序的 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允许我们命名我们的递归过程,但是在最后一个我们得到一个我们需要解决的承诺,因此我们泄露了实现细节并且它变得更加难以使用。通常在涉及 Promise 时,我们希望避免不必要的执行,但是调用应该返回一个 Promise。

    【讨论】:

    • 谢谢。我用 MIT-Scheme 尝试了你的最后一个解决方案,并认为它缺少(延迟)。它输出: ;作为参数传递给 force 的对象 #[compound-procedure 15] 不是一个承诺。
    • @Nick 我在 mit-scheme 9.1.1 中尝试过,它按预期返回了509。由于它是 R5RS,因此您不需要前三行。
    【解决方案2】:

    是的,通过严格的评估,自我应用程序 (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-combinator 进行变体的正常方法是对 self 应用进行 eta-expand,这是另一种延迟它的方法,但不需要显式施加力。相反,强制是由函数应用程序完成的。

    【讨论】:

    • 谢谢!我发布了一个尾递归等价物,但这种范式不起作用。在这里感谢任何指导。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-23
    • 2013-12-30
    • 2020-05-19
    • 2016-10-11
    • 2010-09-29
    • 1970-01-01
    相关资源
    最近更新 更多