The generator function can now be used to send values to the generator.
For example, (define g (generator (yield (yield (yield 1))))) (list (g) (g 2) (g 3) (g 4) (g) (g)) evaluates to '(1 2 3 4 4 4). This is something that Python does (as a generator.send method), which might be useful for using generators as co-routines, and it is actually easy to implement since sending values is exactly what we get when the generator call is actually calling the saved continuation. So most of the change is dealing with the technicalities of throwing an error when the generator is called with some arguments, when that's done after it's terminated (at the stage where it's repeating the last value for ever). A few tests added for this. svn: r17979
This commit is contained in:
parent
18c8e41fac
commit
505034ea26
|
@ -51,24 +51,33 @@
|
|||
|
||||
(define-syntax-rule (generator body0 body ...)
|
||||
(let ()
|
||||
(define (cont)
|
||||
(define (yielder value)
|
||||
(shift-at yield-tag k (set! cont k) value))
|
||||
(reset-at yield-tag
|
||||
(parameterize ([current-yielder yielder])
|
||||
(call-with-values
|
||||
(lambda () (begin body0 body ...))
|
||||
;; only a normal return gets here
|
||||
(case-lambda
|
||||
;; Note: in this case, the generator was invoked with no
|
||||
;; arguments, so returning no values is more symmetric. But
|
||||
;; this is a common case, and probably people would expect a
|
||||
;; void result more than no values.
|
||||
[() (set! cont void)]
|
||||
[(r) (set! cont (lambda () r))]
|
||||
[rs (set! cont (lambda () (apply values rs)))]))
|
||||
(cont))))
|
||||
(define (generator) (cont))
|
||||
(define cont
|
||||
(case-lambda
|
||||
[()
|
||||
(define (yielder value)
|
||||
(shift-at yield-tag k (set! cont k) value))
|
||||
(reset-at yield-tag
|
||||
(parameterize ([current-yielder yielder])
|
||||
(define ret
|
||||
(call-with-values
|
||||
(lambda () (begin body0 body ...))
|
||||
;; get here only on at the end of the generator
|
||||
(case-lambda
|
||||
;; Note: in this case, the generator was invoked with no
|
||||
;; arguments, so returning no values is more symmetric.
|
||||
;; But this is a common case, and probably people would
|
||||
;; expect a void result more than no values.
|
||||
[() void]
|
||||
[(r) (lambda () r)]
|
||||
[rs (lambda () (apply values rs))])))
|
||||
(set! cont (case-lambda
|
||||
[() (ret)]
|
||||
[_ (error 'generator "cannot send values to a ~a"
|
||||
"generator that has terminated")]))
|
||||
(ret)))]
|
||||
[_ (error 'generator
|
||||
"cannot send a value to a generator before it starts")]))
|
||||
(define (generator . xs) (apply cont xs))
|
||||
generator))
|
||||
|
||||
(define-syntax-rule (infinite-generator body0 body ...)
|
||||
|
|
|
@ -235,15 +235,16 @@
|
|||
(for/list ([x (in-generator (helper 0) (helper 1) (helper 2))])
|
||||
x)))
|
||||
|
||||
(let ([g (lambda () (generator (yield 1) (yield 2) (yield 3)))])
|
||||
(let ([g (g)]) (test '(1 2 3) list (g) (g) (g)))
|
||||
(let ([g (g)]) (test '(1 2 3 10 10) list (g) (g) (g) (g 10) (g)))
|
||||
(let ([g (generator (yield (yield (yield 1))))])
|
||||
(test '(1 2 3 4 4 4) list (g) (g 2) (g 3) (g 4) (g) (g))))
|
||||
|
||||
(let* ([helper (lambda (pred num)
|
||||
(for ([i (in-range 0 3)])
|
||||
(yield (pred (+ i num)))))]
|
||||
[g1 (generator
|
||||
(helper odd? 1)
|
||||
(yield 'odd))]
|
||||
[g2 (generator
|
||||
(helper even? 1)
|
||||
(yield 'even))])
|
||||
(for ([i (in-range 0 3)]) (yield (pred (+ i num)))))]
|
||||
[g1 (generator (helper odd? 1) (yield 'odd))]
|
||||
[g2 (generator (helper even? 1) (yield 'even))])
|
||||
(test '(#t #f #f #t #t #f odd even) 'yield-helper
|
||||
(list (g1) (g2) (g1) (g2) (g1) (g2) (g1) (g2))))
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user