使用延续时,`set!` 在 Racket 中如何工作?
How `set!` works in Racket when working with continuations?
我有基于维基百科示例的代码工作代码
(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)))
好的,所以首先注意top-level的东西是(define (yielder) ...)
,而不是 (define yielder ...)
,所以yielder
是一个函数,调用时将 return (thunk ...)
:一个没有参数的函数。这意味着:
(define g (yielder)
导致 g
绑定到那个没有参数的函数。特别是 g
与 control-state
不是一回事,也不是挂起的延续。由于尚未调用该函数,因此尚未发生任何事情。此外 g
永远不会被任何赋值改变:它在 上关闭的 control-state
绑定是 突变的,但是 g
本身的绑定是 nt.
所以,当 g
被 调用时,它会立即调用 control-state
的当前值和当前的延续:一个函数,当被调用时,将立即从 call/cc
和 g
中 return:该函数绑定到 control-state
内的 return
。
control-state
然后调用 (yield 1)
开始计算
(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
的值 1
调用 return
的当前值。 return
然后 return 来自 g
的值:分配尚未(尚未)完成。
下次调用 g
时,它会做同样的事情,创建一个新的 return
延续,并用它作为参数调用 control-state
的当前值。但该值现在是上次调用时藏在那里的延续。所以现在 return 来自 call/cc
的 call/cc
被调用 return
的旧值暂停并在继续之前完成分配...到下一个 yield
除了这次 return 从 g
跳 2
之外,调用再次进行相同的舞蹈。等等。
本质上,这两个延续执行这种交错的小舞蹈,每次调用 g
时,您都会创建一个新的延续,说明如何从该调用中 return,然后调用重新启动主体的延续,在 return 从 g
开始之前为下一步存储新的延续。
这很难理解,但如果你仔细研究一下,也不是不可能。
我有基于维基百科示例的代码工作代码
(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)))
好的,所以首先注意top-level的东西是(define (yielder) ...)
,而不是 (define yielder ...)
,所以yielder
是一个函数,调用时将 return (thunk ...)
:一个没有参数的函数。这意味着:
(define g (yielder)
导致 g
绑定到那个没有参数的函数。特别是 g
与 control-state
不是一回事,也不是挂起的延续。由于尚未调用该函数,因此尚未发生任何事情。此外 g
永远不会被任何赋值改变:它在 上关闭的 control-state
绑定是 突变的,但是 g
本身的绑定是 nt.
所以,当 g
被 调用时,它会立即调用 control-state
的当前值和当前的延续:一个函数,当被调用时,将立即从 call/cc
和 g
中 return:该函数绑定到 control-state
内的 return
。
control-state
然后调用 (yield 1)
开始计算
(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
的值 1
调用 return
的当前值。 return
然后 return 来自 g
的值:分配尚未(尚未)完成。
下次调用 g
时,它会做同样的事情,创建一个新的 return
延续,并用它作为参数调用 control-state
的当前值。但该值现在是上次调用时藏在那里的延续。所以现在 return 来自 call/cc
的 call/cc
被调用 return
的旧值暂停并在继续之前完成分配...到下一个 yield
除了这次 return 从 g
跳 2
之外,调用再次进行相同的舞蹈。等等。
本质上,这两个延续执行这种交错的小舞蹈,每次调用 g
时,您都会创建一个新的延续,说明如何从该调用中 return,然后调用重新启动主体的延续,在 return 从 g
开始之前为下一步存储新的延续。
这很难理解,但如果你仔细研究一下,也不是不可能。