我有以下 Scheme 程序:
(define sg-pair (let ()
(define x #f)
(cons
(call/cc (lambda (c)
(set! x c)))
x)))
(display sg-pair)
(display "\n")
(if (eq? (car sg-pair) (void))
(let ()
((cdr sg-pair) "first")))
(if (string? (car sg-pair))
(let ()
((cdr sg-pair) 2)))
这将打印以下内容:
(#!void . #<procedure #2>)
(first . #<procedure #2>)
(2 . #<procedure #2>)
我目前对于如何构建这一对的理解是:
- 延续 (
c
) 在调用 时创建call/cc
。此时,x
是#f
,并且尚未被评估为 的第二个参数cons
。 lambda
在传递给的的执行期间call/cc
,x
被设置为非 false(继续)值。call/cc
自然返回,并且不返回任何内容(#!void
),并且x
从之前的 set 调用中检索到。- 当后续调用通过 检索的延续时
cdr
,我期望在创建延续时已知的 的映射x -> #f
被恢复。然后,由于lambda
的主体call/cc
不会再次运行,因此x
将保持为 false。 x
将在 之后进行评估call/cc
,这次是#f
,导致第二display
个cdr
不同,并且第二次调用cdr
失败。
x
然而,实际发生的情况是,当重新进入延续时,会记住先前的值。为什么?怎么做?Scheme 实际上使用什么机制来记住环境?
不。没有恢复任何内容。
x
是该表单的本地绑定let
。它有值。无论它有什么值,都是它的价值。不会记住任何先前的值,只保留其当前值,位于绑定指向的内存位置。无论内存状态如何,都不会改变。延续仅与控制流有关。
我们在相同的(词汇)绑定下重新进入代码中的同一点。但这不会影响这些绑定所持有的值。我们只是访问这些绑定,以及它们的值,无论这些绑定当时的值是什么。
调用延续
((cdr sg-pair) value)
相当于但
x
隐藏的本地绑定是相同的x
。其值没有改变。它存储在中(cdr sg-pair)
。因此每次调用((cdr sg-pair) value)
都相当于注意:正如 Shawn 在评论中指出的那样,此代码仅在之前
(cons A B)
进行评估的情况下才能按预期工作。A
B