实施收益并发送计划 [英] Implement yield and send in Scheme
问题描述
我正在尝试将yield
和yield from
从Python移植到Scheme.
I'm trying to port yield
and yield from
from Python to Scheme.
这是我已经完成的实现:
Here is an implementation I've done:
(define (coroutine routine)
(let ((current routine)
(status 'new))
(lambda* (#:optional value)
(let ((continuation-and-value
(call/cc (lambda (return)
(let ((returner
(lambda (value)
(call/cc (lambda (next)
(return (cons next value)))))))
(if (equal? status 'new)
(begin
(set! status 'running)
(current returner))
(current (cons value returner)))
(set! status 'dead))))))
(if (pair? continuation-and-value)
(begin (set! current (car continuation-and-value))
(cdr continuation-and-value))
continuation-and-value)))))
此实现的问题在于,它的调用方式看起来不像Python的yield
.
The problem, with this implementation is that the way it has to be called doesn't looks like Python's yield
.
(define why (call/cc (lambda (yield)
(format #t "love me or leave me!")
(yield "I leave!")
;; the program never reach this part
(format #t "it probably left :("))))
(format #t "return actually populates WHY variable\n")
(format #t "WHY: ~a\n")
除其他事项外,每次需要重新启动协程时,我都必须必须 let
一个新的return
变量以能够exit
协程.基本上,我觉得语法太冗长.还有另一种语法更简洁的方法吗?
Among other things, each time I need to-restart the coroutine, I must let
a new return
variable to be able exit
the coroutine. Basically, I find the syntax too verbose. Is there another to have cleaner syntax?
应该有可能将yield
和和send
值添加到协程.这是必须如何使用协程的示例:
It should be possible to yield
and send
values to the coroutine. Here is an example of how the coroutine must be used:
(define-coroutine (zrange start step)
"compute a range of values starting a START with STEP between
each value. The coroutine must be restarted with 0 or more, which
is added to the step"
(let loop ((n start))
(loop (+ n step (yield n)))))
(coroutine-map (zrange 0 10) '(1 100 1000 10000 100000))
;; => 0 110 1120 11130 111140
在上面,1
被忽略,然后生成器将100
,1000
用作send
.我已经基于@sylwester代码完成了一个实现,但是我在使用宏时遇到了麻烦:
In the above, 1
is ignored and then 100
, 1000
are send
to the generator. I've done an implementation, based on @sylwester code, but I have troubles with the macro:
(define (make-generator procedure)
(define last-return #f)
(define last-value #f)
(define last-continuation (lambda (_) (procedure yield)))
(define (return value)
(newline)(display "fuuu")(newline)
(call/cc (lambda (continuation)
(set! last-continuation continuation)
(set! last-value value)
(last-return value))))
(lambda* (. rest) ; ignore arguments
(call/cc (lambda (yield)
(set! last-return yield)
(apply last-continuation rest)))))
(define-syntax define-coroutine
(syntax-rules ()
((_ (name args ...) body ...)
(define (name args ...)
(make-generator
(lambda (yield)
body ...))))))
(define-coroutine (zrange start step)
(let loop ((n start))
(loop (+ n step (yield n)))))
(display (map (zrange 0 10) '(1 100 1000 10000 100000)))
推荐答案
类似以下内容:
(define (make-generator procedure)
(define last-return values)
(define last-value #f)
(define (last-continuation _)
(let ((result (procedure yield)))
(last-return result)))
(define (yield value)
(call/cc (lambda (continuation)
(set! last-continuation continuation)
(set! last-value value)
(last-return value))))
(lambda args
(call/cc (lambda (return)
(set! last-return return)
(if (null? args)
(last-continuation last-value)
(apply last-continuation args))))))
像这样使用:
(define test
(make-generator
(lambda (collect)
(collect 1)
(collect 5)
(collect 10)
#f)))
(test) ; ==> 1
(test) ; ==> 5
(test) ; ==> 10
(test) ; ==> #f (procedure finished)
现在我们可以将内部结构包装到一个宏中:
Now we can wrap the internals into a macro:
(define-syntax (define-coroutine stx)
(syntax-case stx ()
((_ (name . args) . body )
#`(define (name . args)
(make-generator
(lambda (#,(datum->syntax stx 'yield))
. body))))))
请注意,define-coroutine
是使用语法大小写实现的,因为我们需要使yield
变得不卫生.
Notice that define-coroutine
is implemented using syntax-case since we need to make yield
unhygienic.
(define-coroutine (countdown-from n)
(let loop ((n n))
(if (= n 0)
0
(loop (- (yield n) 1)))))
(define countdown-from-10 (countdown-from 10))
(define (ignore procedure)
(lambda ignore
(procedure)))
(map (ignore countdown-from-10) '(1 1 1 1 1 1)) ; ==> (10 9 8 7 6 5)
;; reset
(countdown-from-10 10) ; ==> 9
(countdown-from-10) ; ==> 8
;; reset again
(countdown-from-10 100) ; ==> 99
这篇关于实施收益并发送计划的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!