【问题标题】:scheme: how to use call/cc for backtracking方案:如何使用 call/cc 进行回溯
【发布时间】:2014-02-18 06:21:07
【问题描述】:

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

有一个名为(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) 函数完全相同。

如果有人能解释我哪里出错了,或者表达一种更好的方法来获得相同的结果,我将不胜感激(我知道有些人讨厌走电话/抄送的方式)。

【问题讨论】:

    标签: scheme callcc


    【解决方案1】:

    将马克吐温的一句错误引语撕成碎片:call/cc 能力的报道被大大夸大了。

    更具体地说,call/cc 捕获调用状态,而不是程序状态。这意味着它捕获有关调用延续时代码流去向的信息。它确实捕获有关变量的信息,特别是,如果您的 get-token 通过 set!ting 变量保存其迭代状态,则在您调用延续时不会恢复。

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

    【讨论】:

    • 非常感谢您解决这个问题。我最终将(peek-token) 函数放在我的扫描仪文件中,并且只是跟踪文件指针,以便我可以从中重新读取令牌。只是出于好奇,您知道在方案或 CL 中捕获实际程序状态的方法吗?还是真的不需要这个?
    • 除非我弄错了 (call-cc (lambda (k) ... (k x))) 形式的任何内容都与 (begin ... x) 完全相同。
    • @jozefg 差不多,假设这是唯一使用延续的地方。
    猜你喜欢
    • 2013-04-09
    • 1970-01-01
    • 2010-12-15
    • 2019-12-30
    • 2016-06-05
    • 1970-01-01
    • 2013-03-14
    • 1970-01-01
    相关资源
    最近更新 更多