Add for/stream and for*/stream comprehensions to racket/stream

Closes #664
This commit is contained in:
Alexis King 2015-12-28 20:53:47 -08:00 committed by Vincent St-Amour
parent 34cfe48355
commit f52d43e600
3 changed files with 50 additions and 1 deletions

View File

@ -1030,6 +1030,26 @@ stream, but plain lists can be used as streams, and functions such as
new stream is constructed lazily.
}
@deftogether[(@defform[(for/stream (for-clause ...) body-or-break ... body)]
@defform[(for*/stream (for-clause ...) body-or-break ... body)])]{
Iterates like @racket[for/list] and @racket[for*/list], respectively, but the
results are lazily collected into a @tech{stream} instead of a list.
Unlike most @racket[for] forms, these forms are evaluated lazily, so each
@racket[body] will not be evaluated until the resulting stream is forced. This
allows @racket[for/stream] and @racket[for*/stream] to iterate over infinite
sequences, unlike their finite counterparts.
@examples[#:eval sequence-evaluator
(for/stream ([i '(1 2 3)]) (* i i))
(stream->list (for/stream ([i '(1 2 3)]) (* i i)))
(stream-ref (for/stream ([i '(1 2 3)]) (displayln i) (* i i)) 1)
(stream-ref (for/stream ([i (in-naturals)]) (* i i)) 25)
]
@history[#:added "6.3.0.9"]
}
@defthing[gen:stream any/c]{
Associates three methods to a structure type to implement the
@tech{generic interface} (see @secref["struct-generics"]) for

View File

@ -57,4 +57,9 @@
(test '(1 3) stream->list (stream-filter odd? '(1 2 3)))
(test '(1 a 2 a 3) stream->list (stream-add-between '(1 2 3) 'a))
(test 4 'for/stream (stream-ref (for/stream ([x '(1 2 3)]) (* x x)) 1))
(test 6 'for*/stream (stream-ref (for*/stream ([x '(1 2 3)] [y '(1 2 3)]) (* x y)) 7))
(test 1 'for/stream (stream-first (for*/stream ([x '(1 0)]) (/ x))))
(test 625 'for/stream (stream-ref (for/stream ([x (in-naturals)]) (* x x)) 25))
(report-errs)

View File

@ -4,6 +4,7 @@
racket/generic
racket/contract/base
racket/contract/combinator
racket/generator
(rename-in "private/for.rkt"
[stream-ref stream-get-generics])
"private/sequence.rkt"
@ -42,7 +43,10 @@
stream-add-between
stream-count
stream/c)
stream/c
for/stream
for*/stream)
(define-syntax gen:stream
(make-generic-info (quote-syntax gen:stream)
@ -300,3 +304,23 @@
(if (chaperone-contract? ctc)
(chaperone-stream/c ctc)
(impersonator-stream/c ctc)))
;; Stream comprehensions -----------------------------------------------------------------------------
(define-syntaxes (for/stream for*/stream)
(let ()
(define ((make-for/stream derived-stx) stx)
(syntax-case stx ()
[(_ clauses . body)
(begin
(when (null? (syntax->list #'body))
(raise-syntax-error (syntax-e #'derived-stx)
"missing body expression after sequence bindings"
stx #'body))
#`(sequence->stream
(in-generator
(#,derived-stx #,stx () clauses
(yield (let () . body))
(values)))))]))
(values (make-for/stream #'for/fold/derived)
(make-for/stream #'for*/fold/derived))))