我对计划函数式编程非常陌生。我最近在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)))
组合函数的结构不匹配。我怎样才能以完全相同的方式在方案中实现它?
答案 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)
相同,您只需应用替换规则即可看到。这里的线索是,这有效地阻止了每一步的计算,直到它实际投入使用。