【问题标题】:Specifics of call/cc通话/抄送的细节
【发布时间】:2010-11-10 07:59:29
【问题描述】:

这与What is call/cc? 相关,但我不想为了自己的目的劫持这个问题,并且它的一些论点(例如与 setjmp/longjmp 的类比)回避了我。

我认为我对什么是延续有足够的了解,我认为它是当前调用堆栈的快照。我不想讨论为什么这可能很有趣,或者你可以用延续做什么。我的问题更具体地说,为什么我必须为 call/cc 提供函数参数?为什么 call/cc 不只返回当前的延续,所以我可以用它做任何我想做的事情(存储它,调用它,你命名它)?在来自另一个问题 (http://community.schemewiki.org/?call-with-current-continuation-for-C-programmers) 的链接中,它谈到“本质上,这只是一种让您继续并避免后续跳转回保存点的干净方式。”,但我没有得到它。这似乎是不必要的复杂。

【问题讨论】:

  • 查看这个帖子,我完全不清楚你在追求什么,以及为什么你发现 LtU 的帖子很有启发性。也许你可以编辑这个问题来更好地解释这些事情?
  • 我认为这个问题仍然准确地反映了我正在努力解决的问题,并且可能是 LtU 海报中的“测试”一词引起了我的注意,因为它让我想到了当您调用它时,只需返回延续。问题中对 schemewiki.org URL 的引用基本上涵盖了所有这些,但正如我当时所说,我没有得到它。它就在我的脸上,但我没有“看到”。理解并不是线性依赖于信息,有时它需要扭转。
  • 好的,明白了!关于理解的曲折路径的观点是公平的,但尝试回答问题却无法弄清楚提问者拒绝的原因有点令人沮丧。

标签: scheme continuations callcc


【解决方案1】:

针对常见的 SO 网络礼节,我正在回答我自己的问题,但更多的是作为编辑而不是答案的提供者。

过了一会儿,我在LtU 开始了一个类似的问题。毕竟,这些都是整天思考语言设计的人,不是吗,answers 之一终于和我一起踢了。现在这里提到的事情,例如由 Eli 或在最初的问题中,对我来说更有意义。这完全取决于延续中包含的内容,以及应用的延续设置的位置。

LtU 的一位posters 写道:

“你可以确切地看到 call/cc 如何让你“不碍事”。使用 em 或 get/cc 你需要做一些测试来确定你是否有后跳或只是初始call. 基本上,call/cc 将延续的使用保留在延续之外,而对于 get/cc 或 em,延续包含它的使用,因此(通常)您需要在延续的开头添加一个测试(即紧跟在 get/cc / em) 之后,将“使用延续部分”与“延续的其余部分”分开。”

这把它带回家了。

还是谢谢你们!

【讨论】:

  • 我认为 Derek 的 em (excluded middle) continuation provider 可以使用 get/cc 来实现。如果我有时间,我会检查一下。
【解决方案2】:

我建议先问问自己:成为一流的接班人意味着什么?

一个表达式的延续本质上由两部分数据组成:首先,该表达式的闭包(即环境);其次,表示应该如何处理表达式的结果。因此,具有一流延续的语言是一种具有封装这些部分的数据结构,并且像对待其他任何数据结构一样对待这些数据结构的语言。

call/cc 是实现这一想法的一种特别优雅的方式:当前的延续被打包为一个过程,该过程封装了 what-is-to-be-done-with-the-expression 是该过程在应用于表达方式;以这种方式表示延续仅仅意味着这个过程的闭包包含它被调用的站点的环境。

您可以想象以其他方式实现一流延续的想法。它们不会是 call/cc,我很难想象这样的表示方法会更简单。

在告别的时候,考虑一下 Eli 提到的 let/cc 的实现,我更喜欢称之为 bind/cc:

(define-syntax bind/cc
    (syntax-rules ()
        ((bind/cc var . body)
             (call/cc (lambda (var) . body)))))

作为一个练习,你将如何基于 bind/cc 实现 call/cc?

【讨论】:

  • 感谢您的帖子,但它离我的问题太远了,无法给我答案。
【解决方案3】:

如果你使用像 Jay 展示的构造,那么你可以抓取延续,但在某种程度上,被抓取的值已经被破坏了,因为你已经在那个延续中。相反,call/cc 可用于获取当前表达式之外仍然未决的延续。例如,延续的最简单用途之一是实现一种abort

(call/cc (lambda (abort)
           (+ 1 2 (abort 9))))

你不能用你描述的操作来做到这一点。如果你尝试一下:

(define (get-cc) (call/cc values))
(let ([abort (get-cc)]) (+ 1 2 (abort 9)))

然后你会得到一个关于将9 应用为过程的错误。发生这种情况是因为 abort 跳回到 let 的新值 9 - 这意味着您现在正在执行第二轮相同的加法表达式,除了现在 abort 绑定到 @ 987654331@...

另外两个相关说明:

  1. 有关延续的实用介绍,请参阅PLAI
  2. call/cc 有点复杂,因为它包含一个函数——在概念上更容易使用的构造是 let/cc,您可以在 PLT Scheme 等一些实现中找到它。上面的例子变成了(let/cc abort (+ 1 2 (abort 9)))

【讨论】:

  • 所以,就我的问题而言,您所说的是(a)如果立即返回延续,则它是“被宠坏的”,因为我在延续之中。但是除非我调用它,否则我不能在延续中。如果延续是一流的值,它们不能被传递它们而被破坏,可以吗?!然后,你说 (b) call/cc 会抓住一个“待定”的延续。一个值怎么可能是未决的?您还说 (c) 延续在当前表达式“之外”。我不太明白。延续捕获多少?一切都达到但不包括呼叫呼叫/抄送?!
  • 是的,您已经在延续中。关于延续的事情是,它们代表当前 call/cc 表达式完成后的“未来”计算——所以在你完成 (call/cc (lambda (k) k)) 之后,你会立即进行延续你刚刚捕获了。
  • 注意,顺便说一句,“延续代表当前堆栈”的观点有助于实现它,但不利于理解它。如果您想理解它,一种更好的查看方式是将其视为上下文。例如,在 (list 1 2 (call/cc (lambda (k) k)) 3 4) 中绑定到 k 的延续是等待答案的上下文,然后将继续评估 3 和 4,然后在 5 个值上应用列表。换句话说,您将其视为:(列表 1 2 [...] 3 4)。这可能很难做到,因为在大多数情况下,此上下文是全局的。请参阅我指出的那些参考资料。
【解决方案4】:

那将不那么通用。如果你想要这种行为,你可以这样做:

(call/cc (lambda (x) x))

您可以查看“Darrell Ferguson 和 Dwight Deugo。“使用当前延续模式调用”中的延续示例用法。第 8 届程序模式语言会议。2001 年 9 月。 (http://library.readscheme.org/page6.html) 并尝试使用上面定义的 call/cc-return 重写它们。

【讨论】:

  • 感谢您展示我如何降级到这种极端情况。但我想要一个解释,而不是解决方案。
猜你喜欢
  • 2010-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-22
  • 2011-04-02
  • 2023-03-20
相关资源
最近更新 更多