究竟什么是"延续提示?"

时间:2015-04-24 03:43:10

标签: scheme racket continuations delimited-continuations

我试图破译the documentation

  

call-with-continuation-prompt

     

proc应用于给定的arg,并通过提示扩展当前延续。提示由prompt-tag标记,其必须是default-continuation-prompt-tag(默认值)或make-continuation-prompt-tag的结果。 proc的结果是call-with-continuation-prompt调用的结果。

我理解它所说的部分"将proc应用于给定arg的当前延续"然后它就是那里的胡言乱语。

对于继续延长,"甚至意味着什么?以及"如何提示"做到这一点"扩展?"

1 个答案:

答案 0 :(得分:44)

概念上什么是提示?

Scheme一般都有 continuation 的概念,但是Racket以分隔的延续的概念扩展了它。延续的想法是它捕获剩余的待计算的计算。我不会试图解释一般的延续,因为这超出了这个问题的范围。

但是,我将解释是什么让分隔延续特殊。通常,捕获延续会捕获整个计算,一直到顶层。这使得它们的用法相对有限,无法实现复杂的控制结构,因为应用延续将完全释放对程序执行的控制。

使用分隔的延续,您只能捕获延续的某个部分。实际捕获的评估部分由提示分隔,其作用类似于当前延续中的标记,用于指定要捕获的延续量。

好的,但这有什么意义呢?

与没有限制的延续相比,实际上没有实际看到分界延续的概念并不十分明确。

标准(非分隔)延续

请考虑以下示例代码。

(define *k* #f)

(sqrt
 (+ 1 2 3
    (call/cc
     (λ (k)
       (set! *k* k)
       0))))

此代码非常简单 - 它捕获延续并存储到全局绑定*k*中。延续本身看起来像这样:

(sqrt (+ 1 2 3 _))

_表示在调用延续时要填写的“洞”。)

应用此延续将完全按照人们的预期工作。

> (*k* 3) ; evaluates (sqrt (+ 1 2 3 3))
3

这一切都非常普通。那么分隔延续带来的差异是什么呢?

定界延续

如果我们只想在*k*中捕获延续的部分,该怎么办?例如,如果我们只想捕获这个延续怎么办?

(+ 1 2 3 _) ; the inner portion of the last continuation

我们可以通过建立一个延续提示来做到这一点,它将调整实际捕获的连续数量。

(sqrt
 (call-with-continuation-prompt
  (λ ()
    (+ 1 2 3
       (call/cc
        (λ (k)
          (set! *k* k)
          0))))))

现在,应用*k*会给出内部结果:

> (*k* 3)
9

分隔连续的类比

Continuations 可以是一个有点抽象的概念,所以如果上面的代码示例不是很清楚,请考虑这个类比。

评估模型是一个堆栈 - 每个函数调用都会将一个新帧推送到堆栈上,并且从一个函数返回会将该帧从堆栈中弹出。我们可以将调用堆栈可视化为一堆卡片。

通常情况下,当捕获延续时,它会捕获当前帧,并所有下面的帧,如下图所示。

未捕获以蓝色表示的顶级。它实际上是分隔系统中的默认提示。

但是,安装新提示会在帧之间创建一种透明分隔符,这会影响作为延续的一部分捕获哪些帧。

此分隔符分隔延续的范围。

附录:提示标签和延续障碍

这是分隔延续的基础知识,但是还有其他方法来控制延续,为延续系统提供更多的权力(以及保护它免受恶意代码),这些是提示标记和延续障碍。

提示标记的概念本质上是一个标记给定提示的“标签”。使用上面的卡片类比,每个透明分隔器都可以给出一个标签。然后,当您捕获延续时,您可以指定一直捕获到特定标签,即使其他标签之间还有其他提示。

另一方面,

延续障碍是一种安全措施。就像提示一样,它们可以被视为位于调用堆栈元素之间的“分隔符”,但不是用作控制堆栈捕获量的标记,而是用作防止连续跳过“跳过”的堆栈。屏障。

有关详细信息,请参阅the section in the Racket reference on continuation barriers。这是一段摘录:

  

具体而言,只有当替换不引入任何延续障碍时,才能用另一个替换。它可以仅通过跳转到当前延续的尾部的延续来消除延续障碍。因此,延续屏障可防止“向下跳跃”进入受屏障保护的延续。