【问题标题】:Change a function into CPS, to not use call/cc将函数更改为 CPS,以不使用 call/cc
【发布时间】:2018-01-27 11:34:48
【问题描述】:

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

我从这个小例子开始了解如何修改代码,以便在没有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(continuation-passing style):

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

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

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

【问题讨论】:

    标签: scheme coroutine continuations continuation-passing callcc


    【解决方案1】:

    使用 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-result6,就像在非CPS 版本中使用let

    我使用 DrRacket,它有一个非常好的调试器,你可以一步一步来看看到底发生了什么。我在+& 调用处添加了一个断点并按下|> 并看到变量three 拳头是3 然后4 无限次。

    CPS 并不容易。 call/cc 为我们提供了 CPS 的好处,而不会使代码更难阅读。在没有call/cc 的情况下实现协程对我来说将是一个挑战,因为我虽然一开始就使用call/cc 来实现它非常复杂,尤其是如果你不是很小心的话,你会有一个无限循环。

    【讨论】:

    • 感谢您的回复。我想要做的是在开始时定义为 0 的 x 变量中存储延续,这样当我使用它时,它就像示例中一样执行 + 2。但是在您的代码中 x 仍然是 0。我该如何更改?
    • @user1241025 x& 确实发生了变化,因为我 set! 它,就像在您的原始代码中一样。否则你会得到一个应用错误。
    猜你喜欢
    • 2019-12-13
    • 1970-01-01
    • 2016-01-24
    • 2019-12-17
    • 2020-01-06
    • 2012-04-12
    • 2016-03-21
    • 2014-09-02
    • 1970-01-01
    相关资源
    最近更新 更多