我有一个基于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-state
是foo5
的本地,好吧,然后我在顶层做(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绑定到没有参数的函数。特别地,g
与control-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
。然后return
从g
的值返回:赋值尚未完成。
下一次调用g
时,它也会执行同样的操作,创建一个新的return
延续,并将其作为参数调用control-state
的当前值。但这个价值现在是上次通话时藏在那里的延续。因此,它现在从因调用return
的旧值而暂停的call/cc
返回,并在继续之前完成赋值。。。除了这次从CCD_ 46返回CCD_。等等。
从本质上讲,这两个continuation执行了这个小的交错舞蹈,对于g
的每个调用,你都在进行一个新的continuation,说明如何从该调用返回,然后调用一个continuation来重新启动身体,在从g
返回之前为下一步隐藏一个新continuation。
这很难理解,但如果你仔细研究一下,也不是不可能。