"continuation prompt?" 到底是什么

What exactly is a "continuation prompt?"

我正在尝试破译the documentation

call-with-continuation-prompt

Applies proc to the given args with the current continuation extended by a prompt. The prompt is tagged by prompt-tag, which must be a result from either default-continuation-prompt-tag (the default) or make-continuation-prompt-tag. The result of proc is the result of the call-with-continuation-prompt call.

我理解它说 "Applies proc to the given args with the current continuation" 的部分,然后它只是胡言乱语。

延续是什么意思 "extended," 以及 "prompt" 如何做到这一点 "extending?"

从概念上讲,什么是提示?

Scheme 通常具有 continuations 的想法,但 Racket 将其扩展为 delimited continuations 的想法。延续的想法是它捕获剩余的计算以供评估。我不会尝试解释一般的延续,因为这超出了这个问题的范围。

但是,我将解释 delimited continuations 的特殊之处。通常,捕获延续会捕获 整个 计算,一直到顶层。这使得它们在实现复杂控制结构方面的使用相对受限,因为应用延续将完全释放对程序执行的控制。

使用定界延续,您只能捕获延续的特定部分。实际捕获的评估部分由 prompts 分隔,其作用类似于当前延续的标记,指定要捕获多少延续。

好的,但这意味着什么?

与未定界延续相比,如果没有实际看到它的实际应用,定界延续的概念并不是很清楚。

标准(非定界)延续

考虑以下示例代码。

(define *k* #f)

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

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

(sqrt (+ 1 2 3 _))

(其中_表示调用continuation时要填写的"hole"。)

应用这一延续将如预期的那样精确地工作。

> (*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 可以 是一个有点抽象的概念,所以如果上面的代码示例不是很清楚,请考虑这个类比。

评估模型是一个堆栈——每个函数调用都会将一个新帧压入堆栈,并从函数返回时将该帧弹出堆栈。我们可以将调用堆栈可视化为一叠卡片。

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

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

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

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

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

这是定界延续的基础知识,但还有其他方法可以控制延续,为延续系统提供更多功能(以及保护它免受恶意代码的侵害),这些是提示标记和延续障碍。

提示标记 的想法本质上是标记给定提示的 "label"。使用上面的卡片类比,可以为每个透明分隔器指定一个标签。然后,当您捕获延续时,您可以指定一直捕获到那个 特定标签 ,即使中间有其他提示和其他标签。

另一方面,

继续障碍是一种安全措施。就像提示一样,它们可以可视化为 "dividers" 位于调用堆栈的元素之间,但它们不是用作控制捕获多少堆栈的标记,而是用作防止连续跳跃的守卫 "through"屏障.

有关这方面的更多详细信息,请考虑阅读 the section in the Racket reference on continuation barriers。以下是摘录:

Specifically, a continuation can be replaced by another only when the replacement does not introduce any continuation barriers. It may remove continuation barriers only through jumps to continuations that are a tail of the current continuation. A continuation barrier thus prevents “downward jumps” into a continuation that is protected by a barrier.