Y Combinator实施方案

时间:2016-05-26 19:21:21

标签: scheme lambda-calculus y-combinator

我对计划函数式编程非常陌生。我最近在lambda演算中遇到了Y-combinator函数,类似于Y ≡ (λy.(λx.y(xx))(λx.y(xx)))。我想在方案中实现它,我搜索了很多,但我没有找到任何完全匹配上述给定结构的实现。我找到的其中一些如下:

(define Y
(lambda (X)
  ((lambda (procedure)
     (X (lambda (arg) ((procedure procedure) arg))))
   (lambda (procedure)
     (X (lambda (arg) ((procedure procedure) arg)))))))

(define Y
  (lambda (r)
    ((lambda (f) (f f))
     (lambda (y)
       (r (lambda (x) ((y y) x)))))))

如您所见,它们与此Y ≡ (λy.(λx.y(xx))(λx.y(xx)))组合函数的结构不匹配。我怎样才能以完全相同的方式在方案中实现它?

1 个答案:

答案 0 :(得分:3)

在像Lazy Racket这样的惰性语言中,您可以使用正常的订单版本,但不能使用任何应用程序订购编程语言(如Scheme)。他们只会陷入无限循环。

Y的应用版本通常称为Z组合子:

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

现在应用这个时发生的第一件事是(g g),因为你总是可以用整个应用程序的扩展替换整个应用程序,可以将函数的主体重写为: p>

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

我还没有改变任何事情。它只是更多的代码完全相同。请注意,此版本使用apply来支持多个参数函数。想象一下阿克曼功能:

(define ackermann
  (lambda (m n)
    (cond
      ((= m 0) (+ n 1))
      ((= n 0) (ackermann (- m 1) 1))
      (else (ackermann (- m 1) (ackermann m (- n 1)))))))

(ackermann 3 6) ; ==> 509

可以使用Z完成此操作:

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

请注意,实现完全相同,不同之处在于如何处理对自身的引用。

修改

所以你问的是评估是如何延迟的。那么正常的订单版本看起来像这样:

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

如果您看一下如何使用参数应用这一点,您会注意到Y永远不会返回,因为它可以在f中应用(f (g g))之前需要评估(g g)反过来评估(f (g g))等。以挽救我们不立即申请(g g)。我们知道(g g)成为一个函数,因此我们只给f一个函数,当应用它时将生成实际函数并应用它。如果你有一个函数add1,你可以创建一个你可以使用的包装器(lambda (x) (add1 x)),它会起作用。以同样的方式,(lambda args (apply (g g) args))(g g)相同,您只需应用替换规则即可看到。这里的线索是,这有效地阻止了每一步的计算,直到它实际投入使用。