【问题标题】:Does call/cc simulate goto this way?call/cc 会以这种方式模拟 goto 吗?
【发布时间】:2015-04-13 20:28:10
【问题描述】:

Lisp in Small Pieces一书中,有如下示例代码,旨在演示call/cc可以模拟goto。

(define (fact n)
   (let ((r 1) (k 'void))
      (call/cc (lambda (c) (set! k c) 'void))
      (set! r (* r n))
      (set! n (- n 1))
      (if (= n 1) r (k 'recurse))))

但是,我不确定我是否误解了某些东西,但我看不出这是 call/cc 模拟 goto 的方式。在最后一行应用k 时,恢复的延续具有原始延续的rn,其值被两个set! 应用程序更改。所以整个循环永远不会终止。

这个例子中的书错了吗?还是我错过了什么?

【问题讨论】:

    标签: scheme lisp continuations callcc


    【解决方案1】:

    恢复的延续具有原始的 r 和 n continuation,其值不会被这两个集合改变! 应用程序。

    不;这是重要的部分;对值的更改可见的。它们没有被重置。我不确定这个问题是否应该被视为重复,但这也出现在call-with-current-continuation - state saving concept 中,提问者指出(查看整个上下文的问题):

    调用 next 3 次会产生 0、1 和 'done。这意味着当 state 使用由 generator 给出的函数 k 时,它没有 恢复程序的状态。

    您可以通过在保存延续后打印 r 和 n 的值来非常简单地对此进行测试。您会看到更新的值在那里。例如:

    (define (fact n)
      (let ((r 1) (k 'void))
        (call-with-current-continuation (lambda (c) (set! k c) 'void))
        (display "r: ") (display r) (newline)
        (display "n: ") (display n) (newline)
        (set! r (* r n))
        (set! n (- n 1))
        (if (= n 1) r (k 'recurse))))
    

    > (fact 6)
    r: 1
    n: 6
    r: 6
    n: 5
    r: 30
    n: 4
    r: 120
    n: 3
    r: 360
    n: 2
    720
    

    相关问题:

    另见:

    【讨论】:

    • IMO 这里没有什么特别之处,k 是一个延续(就了解nr 发生了什么而言)。重要的一点是它是一个闭包,并且它在调用它的上下文中关闭。
    • @NateC-K 好吧,是的,也不是。 call/cc 有点特殊,因为它保存了整个程序状态,除了我们看到的,它不会在保存程序状态时复制环境。没有多少语言有像 call/cc 这样的“保存程序的整个状态”操作,我认为因此,人们常常认为这就像对程序进行快照。问题是它不是真正的快照。这就像将其余的计算捆绑在一个当前环境的闭包中。这就是很多人产生脱节的地方。
    • 这就是我的意思,这就是为什么我建议对此的某些部分答案应该提到闭包。 (您的答案没有,您链接的答案也没有。)
    • 所以,如果您只是将上述评论纳入您的答案,我认为这将是一个更好的答案:)
    • @DongFeng 在使用像 Scheme 这样高级的语言时,放弃“在堆栈上分配”和类似的语义。关键是set! 改变了绑定的,所以所有关闭这些绑定的作用域都会看到set! 的结果。
    猜你喜欢
    • 2011-08-25
    • 2010-12-15
    • 1970-01-01
    • 2016-03-21
    • 1970-01-01
    • 2012-03-28
    • 1970-01-01
    • 1970-01-01
    • 2015-09-29
    相关资源
    最近更新 更多