【问题标题】:Scheme: how does a nested call/cc work for a coroutine?方案:嵌套调用/cc 如何为协程工作?
【发布时间】:2012-10-31 13:44:19
【问题描述】:

我正在查看来自http://community.schemewiki.org/?call-with-current-continuation 的协程的以下示例:

 (define (hefty-computation do-other-stuff) 
    (let loop ((n 5)) 
      (display "Hefty computation: ") 
      (display n) 
      (newline) 
      (set! do-other-stuff (call/cc do-other-stuff)) ; point A
      (display "Hefty computation (b)")  
      (newline) 
      (set! do-other-stuff (call/cc do-other-stuff)) 
      (display "Hefty computation (c)") 
      (newline) 
      (set! do-other-stuff (call/cc do-other-stuff)) 
      (if (> n 0) 
          (loop (- n 1))))) 

多余的工作:

 ;; notionally displays a clock 
 (define (superfluous-computation do-other-stuff) 
    (let loop () 
      (for-each (lambda (graphic) 
                  (display graphic) 
                  (newline) 
                  (set! do-other-stuff (call/cc do-other-stuff))) 
                '("Straight up." "Quarter after." "Half past."  "Quarter til.")) ; point B
      (loop))) 


(hefty-computation superfluous-computation) 

对于 call/cc 的第一次使用,上下文应该是什么?当我说上下文时,我的意思是由于 callcc 的跳转,我们应该“返回”到哪里?

据我了解,第一次调用 call/cc 时,do-other-stuff 本质上变成了一个过程,执行多余计算的代码,然后在集合之后立即跳转到该点! (A点)。第二次,它将“跳转到 B 点”行为包裹在“跳转到 A 点并执行上下文,或 A 点之后的任何代码”周围。这是正确的吗?

如果设置,这段代码似乎不起作用!实际发生了。或者是套装!这段代码需要运行吗?

对正在发生的事情进行可视化表示真的很有帮助。

【问题讨论】:

    标签: scheme coroutine callcc continuation


    【解决方案1】:

    call/cc 的上下文是调用 call/cc 的地方。您几乎可以想到一个call/cc,例如goto,它将代码直接跳回到您之前的位置,并用返回值替换(call/cc whatever)

    call/cc 基本上是说,“让我们去做这个功能,然后把它交给这里,然后跳回这里,忘记它在做什么”

    好吧,当我第一次尝试理解call/cc时,我发现这段代码非常混乱,所以让我们看一个简化的协程示例:

    (define r1
      (lambda (cont)
        (display "I'm in r1!")
        (newline)
        (r1 (call/cc cont))))
    
    (define r2
      (lambda (cont2)
        (display "I'm in r2!")
        (newline)
        (r2 (call/cc cont2))))
    

    好的,这与您的代码概念完全相同。但它要简单得多。

    在这种情况下,如果我们调用 (r1 r2) 会打印出来

    I'm in r1
    I'm in r2
    I'm in r1
    I'm in r2    
    I'm in r1
    I'm in r2    
    I'm in r1
    I'm in r2
    ...
    

    为什么?因为r1 首先将 r2 视为cont,所以它向我们宣布它在 r1 中。然后它会以(call/cc cont) aka (call/cc r2) 的结果自行递归。

    好的,那么返回的结果是什么? (call/cc r2) 将开始执行 r2 并宣布它在 r2 中,然后使用 (call/cc cont2) 的结果递归自身。好的,cont2 又是什么? cont2 是之前 r1 中该表达式的延续。因此,当我们在这里调用它时,我们会将一个延续传回我们当前所在的位置。然后我们忘记了我们在 r2 中所做的任何事情并跳回到执行 r1。

    这在 r1 现在重复。我们宣布内容,然后跳回到我们之前在 r2 中的位置和我们之前的表达式,(call/cc cont2) 返回一个延续到我们在r1 中的位置,然后我们继续我们快乐的无限循环。

    回到你的代码

    在您的代码中,概念完全相同。其实superfluous-computation与上面的功能几乎是一模一样的,你停下来想一想。那么set!s 是怎么回事?在这段代码中,他们所做的只是将do-other-work 的值更改为最新的延续。而已。在我的示例中,我使用了递归。在这个例子中,他们使用set!

    【讨论】:

    • 当你说“cont2 是之前在 r1 中的那个表达式的延续”时,你是说 cont2 是一个跳回到行 r1 (call/cc cont) 的过程,除了 (call/cc cont) 被替换为跳转到 r2 即将递归的过程?在您真正完成 r1 (call/cont) 的结果之前,我无法理解 call/cc cont 最终评估的结果。
    • (cont2 val)类似于说跳回r1 (call/cc cont)并用val替换(call/cc cont)
    • 真的,当我说替换时,我的意思是 (call/cc cont) 评估为 val。如果有帮助,您可以将其视为替代品
    • 好的,所以如果我在执行 call/cc cont2 时理解正确,我们执行跳转到 (r1 val),其中 val 是 r2 的延续。然后我们递归到 r1,cont 变成“跳转到 r2 将递归调用自身的地方”?非常感谢您迄今为止的帮助,我真的很感激。我花了一整天的时间试图弄清楚事情是如何运作的。
    • 是的,这就是一般的想法,它也帮助我用 2 个手指跟随,1 个是执行的内容,1 个是下一个继续跳转的位置
    猜你喜欢
    • 1970-01-01
    • 2021-06-13
    • 2020-10-09
    • 1970-01-01
    • 2015-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多