多好啊在Racket中使用continuations



我有一个基于wikipedia 示例的代码

(define (foo5)
(define (control-state return)
(define-syntax-rule
(yield x) 
(set! return (call/cc
(lambda (resume-here)
(set! control-state resume-here)
(return x)))))
(yield 'foo)
(yield 'bar)
(yield 'tar)
(return 'end))
(thunk (call/cc control-state)))

我永远不会自己实现这个代码,因为set!的使用违背了我内心的每一种直觉

首先,control-statefoo5的本地,好吧,然后我在顶层做(define g (foo5)),在这一点上,在我的脑海中,g指向与control-state相同的引用,一个内存中的闭包。

然后我把g称为(g),它的值一直到(set! control-state resume-here),此时我的直觉就崩溃了。在我的脑海中,这会将内部control-state符号设置为resume-here,但它也会更改外部g?。这怎么可能呢?

这比看起来更简单。

首先,让我们重写它,给它一个更明显的名称,并用一个函数替换本地宏,以消除可能需要宏的任何混淆:

(define (yielder)
(define (control-state return)
(define (yield x)
(set! return (call/cc (λ (resume-here)
(set! control-state resume-here)
(return x)))))
(yield 1)
(yield 2)
(yield 3)
(return 'end))
(thunk (call/cc control-state)))

好吧,首先要注意的是,顶级的是(define (yielder) ...),而不是(define yielder ...),所以yielder是一个函数,当被调用时,它将返回(thunk ...):一个没有参数的函数。这意味着:

(define g (yielder)

导致CCD_ 17绑定到没有参数的函数。特别地,gcontrol-state不是一回事,也不是一个挂起的延续。由于函数尚未调用,因此尚未发生任何事情。此外,g从未因任何赋值而改变:它在上闭合的control-state结合是突变的,但g本身的结合是nt

因此,当调用g时,它会立即调用具有当前延续的control-state的当前值:当调用该函数时,该函数将立即从call/cc返回,从而从g返回:该函数在control-state内绑定到return

control-state然后调用(yield 1),CCD_30开始评估

(set! return (call/cc (λ (resume-here)
(set! control-state resume-here)
(return x)))))

因此,这涉及到调用

(λ (resume-here)
(set! control-state resume-here)
(return x))

resume-here绑定到一个延续,如果调用该延续,则会将其调用的值分配给return。它将这个延续存储到control-state中,然后用x的值调用return的当前值,即1。然后returng的值返回:赋值尚未完成。

下一次调用g时,它也会执行同样的操作,创建一个新的return延续,并将其作为参数调用control-state的当前值。但这个价值现在是上次通话时藏在那里的延续。因此,它现在从因调用return的旧值而暂停的call/cc返回,并在继续之前完成赋值。。。除了这次从CCD_ 46返回CCD_。等等。

从本质上讲,这两个continuation执行了这个小的交错舞蹈,对于g的每个调用,你都在进行一个新的continuation,说明如何从该调用返回,然后调用一个continuation来重新启动身体,在从g返回之前为下一步隐藏一个新continuation。

这很难理解,但如果你仔细研究一下,也不是不可能。

相关内容

最新更新