代码之家  ›  专栏  ›  技术社区  ›  geckos

多好啊在Racket中使用continuations?

  •  0
  • geckos  · 技术社区  · 2 年前

    我有一个基于维基百科的例子的代码

    (define (foo5)
      (define (control-state return)
        (define-syntax-rule
          (yield x) 
          (set! return (call/cc
                        (lambda (resume-here)
                          (set! control-state resume-here)
                          (return x)))))
        (yield 'foo)
        (yield 'bar)
        (yield 'tar)
        (return 'end))
      (thunk (call/cc control-state)))
    

    我永远不会自己实现这个代码,因为 set! 违背了我内心的每一种直觉。

    第一 control-state 是本地的 foo5 ,好吧,那我愿意 (define g (foo5)) 在最高级别,在这一点上,在我的脑海中 g 指向与 控制状态 点,记忆中某个地方的闭包。

    然后我打电话 g (g) 它的评估值高达 (set! control-state resume-here) 在这一点上,我的直觉崩溃了。在我的脑海中,这将设置内部 控制状态 符号到 resume-here ,但它改变了外部 g 还有?。这怎么可能呢?

    0 回复  |  直到 2 年前
        1
  •  1
  •   ignis volens    2 年前

    这比看上去更简单。

    首先,让我们重写它,给它一个更明显的名称,并用一个函数替换本地宏,以消除可能需要宏的任何混淆:

    (define (yielder)
      (define (control-state return)
        (define (yield x)
          (set! return (call/cc (λ (resume-here)
                                  (set! control-state resume-here)
                                  (return x)))))
        (yield 1)
        (yield 2)
        (yield 3)
        (return 'end))
      (thunk (call/cc control-state)))
    

    好的,首先要注意的是 (define (yielder) ...) (define yielder ...) 所以 yielder 是一个函数,当被调用时将返回 (thunk ...) :一个没有参数的函数。这意味着:

    (define g (yielder)
    

    原因 g 绑定到没有参数的函数。特别是 g control-state ,也不是暂停的延续。由于函数尚未调用,因此尚未发生任何事情。进一步的 g 从未因任何分配而更改: 控制状态 绑定它关闭 突变,但 g 它本身就是nt。

    所以,当 g 调用,则它立即调用的当前值 控制状态 带有当前continuation:一个函数,当被调用时,将立即从 call/cc 因此 g :该函数绑定到 return 在…内 控制状态

    控制状态 然后打电话 (yield 1) 开始评估

    (set! return (call/cc (λ (resume-here)
                            (set! control-state resume-here)
                            (return x)))))
    

    所以这需要打电话

    (λ (resume-here)
      (set! control-state resume-here)
      (return x))
    

    具有 resume-here 绑定到一个延续,如果调用该延续,将导致将其调用的值分配给 回来 .它将此延续隐藏到 控制状态 ,然后调用的当前值 回来 值为 x 哪个是 1 回来 然后从该值返回 g :作业尚未完成。

    下次 g 它做同样的事情,创造一个新的 回来 continuation并调用的当前值 控制状态 以它作为论据。但这个价值现在是上次通话时藏在那里的延续。所以现在这个从 呼叫/cc 由于旧价值的调用而暂停 回来 并在继续之前完成任务。。。到下一个 yield 除了这次回来之外,哪个电话又跳了同样的舞 2 从…起 g 等等

    从本质上讲,这两个连续部分表演了这种小的交错舞蹈,你每次都会听到 g 您正在创建一个新的continuation,说明如何从该调用返回,然后调用一个continuation来重新启动主体,在从返回之前为下一步隐藏一个新continuation g

    这很难理解,但如果你仔细研究一下,也不是不可能。