【问题标题】:Y Combinator implementation SchemeY Combinator 实现方案
【发布时间】:2016-05-26 19:21:21
【问题描述】:

我对方案函数式编程真的很陌生。我最近在 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))) 组合函数的结构不匹配。如何以完全相同的方式在方案中实现它?

【问题讨论】:

  • 你不能,因为传统的 Y 组合器不适用于应用订单评估。
  • Y ≡ (λy.(λx.y(xx))(λx.y(xx))) 版本假定惰性求值。延迟函数评估的一种方法是将函数表达式 e 包装在 (lambda (arg) (e arg)) 中,因此在这种情况下,函数表达式 (procedure procedure) 被重写为 (lambda (arg) ((procedure procedure) arg))
  • @AlexKnauth 因此,如果我有一个递归加法函数可以将 0 到 n 数字相加,如下所示:(define (REC procedure iterations) ( (ISZERO iterations) 0 (+ iterations (procedure (- iterations 1))) ) ) 我如何在方案中将它与 Y-combinator 一起使用?
  • 这可能应该是一个单独的问题(而且,我认为您缺少if)。但一般来说,您会将procedure 定义为(Y (lambda (procedure) ....))
  • 也许你应该阅读这个问题:Y combinator discussion in “The Little Schemer”。它说明了为什么只使用 (procedure procedure) 不起作用。

标签: scheme lambda-calculus y-combinator


【解决方案1】:

在像 Lazy Racket 这样的惰性语言中,您可以使用正常顺序版本,但不能使用像 Scheme 这样的任何应用顺序编程语言。它们只会进入无限循环。

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

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

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

(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 (g g)) 中应用 f 之前它需要评估 (g g) 进而评估 (f (g g)) 等. 挽救我们没有立即申请(g g)。我们知道(g g) 变成了一个函数,所以我们只给f 一个函数,当应用它时会生成实际的函数并应用它。如果你有一个函数add1,你可以制作一个包装器(lambda (x) (add1 x)),你可以使用它来代替它,它会起作用。以同样的方式(lambda args (apply (g g) args))(g g) 相同,您只需应用替换规则即可看到这一点。这里的线索是,这有效地停止了每一步的计算,直到它真正投入使用。

【讨论】:

  • 你能告诉我“将正常顺序Y组合器转换为Z组合器”如何解决类似scheme的应用顺序语言中的无限循环问题?
  • @QandeelAbbasi 我添加了更多关于如何避免无限循环的信息。
猜你喜欢
  • 2019-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-07
  • 1970-01-01
相关资源
最近更新 更多