为什么这个生成器实现在 Racket 中不起作用?
Why this generator implementation does not work in Racket?
我正在尝试使用 call/cc
在球拍中实现生成器,目前我有这段代码
(define (foo2)
(define (g abort)
(define-syntax-rule
(yield x)
(call/cc (lambda (k)
(set! g k)
(abort x))))
(yield 'foo)
(yield 'bar)
(yield 'tar)
(abort 'end))
(thunk (call/cc g)))
如果我在 REPL 中使用它,它似乎工作正常
foo.rkt> (define g (foo2))
foo.rkt> (g)
'foo
foo.rkt> (g)
'bar
foo.rkt> (g)
'tar
foo.rkt> (g)
'end
foo.rkt> (g)
'end
foo.rkt>
但是如果我尝试在列表中调用 g,它就会挂起,我不知道发生了什么
foo.rkt> (define g (foo2))
foo.rkt> (list (g) (g) (g))
;; prompt is not released
这是怎么回事?我的生成器实现哪里错了?
什么是(list (g) (g) (g))
?嗯,在 CPS 中基本上是这样的:
(g& (lambda (g1) ; continuation g1
(g& (lambda (g2)
(g& (lambda (g3)
(list& g1 g2 g3 halt))))
你正在替换 g&
,但你们所有人 yield
共享相同的首字母 abort
,即延续 g1
,你们永远不会达到延续 g2
因为每次 abort
都叫你 运行 继续 g1
。如果您单步执行,您会注意到这个延续得到了所有的收益值,但最终它调用了 abort
。即使您删除了最后一个调用,它也始终会在您的表达式中调用第二个 (g)
。
有助于了解 call/cc&
是这样的:
(define (call/cc& f cc)
(f (lambda (v ignored-cc)
(cc v))
cc))
现在 g
在 CPS 中看起来像这样。请注意,abort
是其中的自由变量,即使您替换 g&
,它们也是相同的
(define (g& abort cont)
(call/cc& (lambda (k cc)
(set!& g&
k
(lambda (undefined)
(abort 'foo cc))))
(lambda (ignored1)
(call/cc& (lambda (k cc)
(set!& g&
k
(lambda (undefined)
(abort 'bar cc))))
(lambda (ignored2)
(call/cc& (lambda (k cc)
(set!& g&
k
(lambda (undefined)
(abort 'tar cc))))
(lambda (ignored3)
(abort 'end cont)))))))))
作为解决方法,我想您需要控制设置为 g
的内容。例如。代替
(set! g k)
可能是这样的:
(set! g (lambda (new-abort)
(set! abort new-abort)
(k 'ignored)))
然后列表表达式按预期工作。为什么它在 REPL 中起作用是由于 continuation prompts.
我正在尝试使用 call/cc
在球拍中实现生成器,目前我有这段代码
(define (foo2)
(define (g abort)
(define-syntax-rule
(yield x)
(call/cc (lambda (k)
(set! g k)
(abort x))))
(yield 'foo)
(yield 'bar)
(yield 'tar)
(abort 'end))
(thunk (call/cc g)))
如果我在 REPL 中使用它,它似乎工作正常
foo.rkt> (define g (foo2))
foo.rkt> (g)
'foo
foo.rkt> (g)
'bar
foo.rkt> (g)
'tar
foo.rkt> (g)
'end
foo.rkt> (g)
'end
foo.rkt>
但是如果我尝试在列表中调用 g,它就会挂起,我不知道发生了什么
foo.rkt> (define g (foo2))
foo.rkt> (list (g) (g) (g))
;; prompt is not released
这是怎么回事?我的生成器实现哪里错了?
什么是(list (g) (g) (g))
?嗯,在 CPS 中基本上是这样的:
(g& (lambda (g1) ; continuation g1
(g& (lambda (g2)
(g& (lambda (g3)
(list& g1 g2 g3 halt))))
你正在替换 g&
,但你们所有人 yield
共享相同的首字母 abort
,即延续 g1
,你们永远不会达到延续 g2
因为每次 abort
都叫你 运行 继续 g1
。如果您单步执行,您会注意到这个延续得到了所有的收益值,但最终它调用了 abort
。即使您删除了最后一个调用,它也始终会在您的表达式中调用第二个 (g)
。
有助于了解 call/cc&
是这样的:
(define (call/cc& f cc)
(f (lambda (v ignored-cc)
(cc v))
cc))
现在 g
在 CPS 中看起来像这样。请注意,abort
是其中的自由变量,即使您替换 g&
(define (g& abort cont)
(call/cc& (lambda (k cc)
(set!& g&
k
(lambda (undefined)
(abort 'foo cc))))
(lambda (ignored1)
(call/cc& (lambda (k cc)
(set!& g&
k
(lambda (undefined)
(abort 'bar cc))))
(lambda (ignored2)
(call/cc& (lambda (k cc)
(set!& g&
k
(lambda (undefined)
(abort 'tar cc))))
(lambda (ignored3)
(abort 'end cont)))))))))
作为解决方法,我想您需要控制设置为 g
的内容。例如。代替
(set! g k)
可能是这样的:
(set! g (lambda (new-abort)
(set! abort new-abort)
(k 'ignored)))
然后列表表达式按预期工作。为什么它在 REPL 中起作用是由于 continuation prompts.