将功能更改为CPS,不使用call / cc

时间:2017-08-18 17:55:52

标签: scheme coroutine continuations continuation-passing callcc

我想了解如何在没有call/cc的情况下实现 coroutines

我从这个小例子开始,了解如何修改代码,以便我可以在没有call/cc的情况下使用它:

(define x 0)

( + 2 (call-with-current-continuation
  (lambda (cont)
    (set! x cont)
  3)))

(x 4)

当我执行call/cc函数时,它给我 5 ,然后执行(x 4) 6

我使用此功能替换call/cc

(define (call/cc-cps f continuation)
   (f continuation continuation))

我尝试将此功能更改为CPS(继续传递样式):

(call/cc-cps
  (lambda (exit cont)
   (set! x cont)
   3)
(lambda (value)
  (+ value 2)))

但是当我执行它时,而不是 5 我得 3 。但我确实使用(x 4) 6

你能告诉我有什么问题吗? 谢谢

1 个答案:

答案 0 :(得分:1)

使用CPS,您不会有顶级延续提示,因此为了进行比较,您需要将代码放在空的内容中,如下所示:

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

注意&amp ;.它用于表示该函数需要一个额外的参数,即延续。在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计算的无限次为6,就像在非CPS版本中一样与let

我使用DrRacket,它有一个非常好的调试器,你可以步骤,看看究竟发生了什么。我在+&来电时添加了一个断点,然后点击|>,看到变量three拳头为3,然后4无限次。

CPS并不容易。 call/cc为我们提供了CPS的好处,而不会使代码更难以阅读。在没有call/cc的情况下实现协同程序对我来说很难写,因为我开始使用call/cc这一点非常复杂,特别是因为如果你不是非常小心,你会有一个无限循环。