for-helpers
Helper macros for racket/for.
1 APIs
(require for-helpers) | package: for-helpers |
syntax
(in-mapped proc sequence ...+)
(in-generator (let ([p proc]) (for ([s sequence] ...) (call-with-values (λ () (p s ...)) yield))))
This macro cannot be used outside for clauses.
> (for/list ([(a b) (in-mapped (match-lambda [(cons a b) (values a b)]) '((1 . 2) (3 . 4) (4 . 5)))]) (cons a b)) '((1 . 2) (3 . 4) (4 . 5))
> (for/list ([a (in-mapped cons (in-mapped cons (in-range 5) (in-range 5 10)) (in-mapped cons (in-range 10 15) (in-range 15 20)))]) a) '(((0 . 5) 10 . 15) ((1 . 6) 11 . 16) ((2 . 7) 12 . 17) ((3 . 8) 13 . 18) ((4 . 9) 14 . 19))
syntax
(in-filtered pred sequence ...+)
This macro cannot be used outside for clauses.
> (for/list ([(a b) (in-filtered < '(1 3 5 -1) (in-list '(6 4 2 0)))]) (cons a b)) '((1 . 6) (3 . 4) (-1 . 0))
> (for/list ([a (in-filtered odd? (in-range 5))] [b (in-filtered even? (in-range 5))]) (cons a b)) '((1 . 0) (3 . 2))
1.1 Performance Notes
Due to the limitations of code structures of :do-in, in-mapped and in-filtered do not compose well without optimizations. This package does optimize nested forms, which should cover most use cases. See tests/bench.rkt.
> (optimize [(a) (in-filtered positive? (in-filtered even? (in-range -5 5)))])
((a)
(in-filtered
(let ((positive?30 positive?))
(let ((even?31 even?)) (λ (v29) (and (even?31 v29) (positive?30 v29)))))
(in-range -5 5)))
> (optimize [(a) (in-mapped cons (in-mapped cons (in-range 500) (in-range 500 1000)) (in-mapped cons (in-range 1000 1500) (in-range 1500 2000)))])
((a)
(in-mapped
(let ((cons33 cons))
(let ((cons34 cons))
(let ((cons37 cons))
(λ (tmp35 tmp36 tmp38 tmp39)
(cons33 (cons34 tmp35 tmp36) (cons37 tmp38 tmp39))))))
(in-range 500)
(in-range 500 1000)
(in-range 1000 1500)
(in-range 1500 2000)))
> (optimize [(a) (in-filtered odd? (in-mapped add1 (in-filtered even? (in-mapped (λ (v) (* 2 v)) (in-range 10)))))])
((a)
(in-filter&map
(let ((odd?42 odd?))
(let ((add143 add1))
(let ((even?45 even?))
(let ((temp46 (λ (v) (* 2 v))))
(λ (v40)
(let ((tmp44 (temp46 v40)))
(if (even?45 tmp44)
(let ((tmp41 (add143 tmp44)))
(if (odd?42 tmp41) (values #t tmp41) (values #f #f)))
(values #f #f))))))))
(in-range 10)))
Currently, it is not suggested to define something like in-filter-mapped as (in-filtered values (in-mapped _ ...)) using define-sequence-syntax, since there is no partial expansion support from expand-for-clause, which disables potential optimizations when nested.
2 More APIs
(require for-helpers/extra) | package: for-helpers |
syntax
(in-filter&map proc sequence ...+)
(in-generator (let ([p proc]) (for ([s sequence] ...) (let-values ([(ok x ...) (proc s ...)]) (when ok (yield x ...))))))
This macro cannot be used outside for clauses.
> (for/list ([(a b) (in-filter&map (λ (a b) (values (< a b) (+ a b) (- a b))) (in-list '(1 3 5 -1)) (in-list '(6 4 2 0)))]) (cons a b)) '((7 . -5) (7 . -1) (-1 . -1))
syntax
(in-lists sequence)
(in-generator (for* ([s sequence] [x (in-list s)]) (yield x)))
This macro cannot be used outside for clauses.
> (for/list ([x (in-lists (in-lists (in-list '(((1 2 3) (4 5 6)) ((7 8 9) (a b c))))))]) x) '(1 2 3 4 5 6 7 8 9 a b c)
syntax
(in-nested ([(s ...) sequences] ...) sequence)
This macro cannot be used outside for clauses.
2.1 Performance Notes for for-helpers/extra
Benchmarks show in RacketCS, the performance of in-lists is nearly optimal, and in-nested is also reasonably good. But unfortunately, it seems RacketBC doesn’t properly optimize these forms, which results in poor performance.