使用 CPS,您没有顶级延续提示,因此为了进行比较,您需要将代码放在一个空的 let 中,如下所示:
(let ()
(define x 0)
(+ 2 (call-with-current-continuation
(lambda (cont)
(set! x cont)
3))) ; ==> 5
(x 4))
; ==> 6
; ==> 6
; ==> 6
; ... forever
这成为一个无限循环,因为您(x 4)
每次都在求和之后调用。
;; CPS-version of +
(define (+& a b continuation)
(continuation (+ a b)))
注意 &。它用于表示该函数需要一个额外的参数,即延续。在 CPS 中,您可以call/cc
这样定义:
(define (call/cc& f& continuation)
(define (exit& value actual-continuation)
(continuation value))
(f& exit& continuation))
请注意,它与您的版本完全不同。它将退出函数传递给f&
而不是使用传递的函数来actual-continuation
完成所有剩余的计算,而是将它传递给继续call/cc&
。
现在我们可以将你的程序改写成这样:
((lambda (x& halt-continuation)
(call/cc& (lambda (cont continuation)
((lambda (ingnored-undefined-value) (continuation 3))
(set! x& cont)))
(lambda (three)
(+& 2 three (lambda (ignored-result)
(x& 4 halt-continuation))))))
0 values)
ignored-result
是5
第一次和无限次,它用4
ignored-result
is计算它6
,就像在非 CPS 版本中一样let
。
我使用 DrRacket,它有一个非常好的调试器,你可以一步一步来看看到底发生了什么。我在通话中添加了一个断点+&
并按下并|>
看到可变three
拳头是无限次。3
4
CPS 并不容易。call/cc
为我们提供了 CPS 的好处,而不会使代码更难阅读。在没有协同程序的情况下实现call/cc
对我来说是一个挑战,因为我一开始就很复杂call/cc
,特别是如果你不小心就会出现无限循环。