方案:如何使用call / cc进行回溯

时间:2014-02-18 06:21:08

标签: scheme callcc

在过去的几天里,我一直在玩计划(特别是诡计)中的延续,并且对某些功能的结果感到有些困惑,并且想知道是否有人能够准确地解释这里发生了什么。

有一个名为(get-token)的函数将检索给定文件中的下一个找到的标记。例如,如果接下来的3个标记是“a”,“b”和“c”,则调用(get-token)将在第一次调用时返回“a”,第二次调用时返回“b”,和“c”第三次被召唤。

我想要做的是有一个函数(peek-token),它将调用(get-token),返回令牌,然后返回调用(get-token)函数之前的状态。我尝试了许多不同的方法来实现这个结果,而我现在拥有的是:

;; make things a little easier to write
(define-syntax bind/cc
  (syntax-rules ()
    ((bind/cc var . body)
     (call/cc (lambda (var) . body)))))

;; function should return next token and then
;; revert to previous state
(define (peek-token)
  (bind/cc return
           (let ((token (get-token)))
             (return token))))

我现在如何理解,bind/cc将在第一个return保存延续,然后执行以下代码块。然后,当再次点击return时,程序会跳回到延续绑定的位置,并且结果会给出token值。

但是,当我运行上述函数时,结果与原始(get-token)函数完全相同。

如果有人能够解释我出错的地方,或者表达更好的方法来获得相同的结果(我知道有些人讨厌通过电话/ cc的方式),我将非常感激。

1 个答案:

答案 0 :(得分:4)

将马克·吐温错误引用为碎片:call/cc的能力报告被夸大了。

更具体地说,call/cc捕获呼叫状态,而非程序状态。这意味着它会捕获有关在调用continuation时代码流的位置的信息。它捕获有关变量的信息,特别是,如果get-token通过set!变量保存其迭代状态,则在调用时不会恢复你的继续。

事实上,(call/cc (lambda (k) (let ((token (get-token))) (k token))))的表达应该与(get-token)的行为相同;两个表达式之间不应存在任何可观察到的差异。