Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 112 lines (103 sloc) 4.447 kB
0bec1f6 @greghendershott Add in-take-list sequence.
authored
1 #lang racket
2
bd15932 @greghendershott Stream-based implementation.
authored
3 (require racket/generator)
4
f705155 @greghendershott Delete filter-take
authored
5 (provide in-take)
0bec1f6 @greghendershott Add in-take-list sequence.
authored
6
7 ;; Background: Functions like `hash', `hash-set*', `dict-set*' and so
8 ;; on take argument couples as alternating arguments rather than a
9 ;; cons pair. Using such functions can be refreshing, as you can type
10 ;; simply (hash k0 v0 k1 v1) instead of all the dots and parens with
f705155 @greghendershott Delete filter-take
authored
11 ;; #hash([k0 . v0][k1 v2]).
0bec1f6 @greghendershott Add in-take-list sequence.
authored
12 ;;
f705155 @greghendershott Delete filter-take
authored
13 ;; Using such functions is nice, but defining them is a bit awkward --
14 ;; unless you have in-take. It lets you sequence a flat list of items
15 ;; in groups of N, much like using `take' and `drop'.
16 ;;
17 ;; Likewise, this makes it easy to write simple flat wrappers. For example
0bec1f6 @greghendershott Add in-take-list sequence.
authored
18 ;; here is a `hash'-like initializer for association lists:
19 ;;
20 ;; (define (alist . xs)
bd15932 @greghendershott Stream-based implementation.
authored
21 ;; (for/list ([(k v) (in-take xs 2)])
0bec1f6 @greghendershott Add in-take-list sequence.
authored
22 ;; (cons k v)))
23 ;;
7a2028d @greghendershott Describe the generalizing in comments.
authored
24 ;; Generalizing:
25 ;;
26 ;; 1. At first I wrote this for couples in lists.
27 ;;
28 ;; 2. Then I generalized it to any group size -- couples, triples,
29 ;; whatever -- in lists.
30 ;;
f705155 @greghendershott Delete filter-take
authored
31 ;; 3. Then I generalized it to any single-valued sequence: list,
32 ;; vector, string, etc.
0bec1f6 @greghendershott Add in-take-list sequence.
authored
33
34 (define (make-fill who n)
35 (lambda (_)
36 (error who "list not multiple of ~a items" n)))
37
38 (define fill/c (exact-nonnegative-integer? . -> . any/c))
39
bd15932 @greghendershott Stream-based implementation.
authored
40 ;; (in-take seq n fill) is a way to take `n' elements at a time from
41 ;; `seq'. For instance with n=2 you will get successive couples of
42 ;; elements, with n=3 you get triples, and so on. `n' defaults to 2.
43 ;; If there aren't exactly `n' elements at the end of the list, `fill'
44 ;; is called with an index from 0 to (sub1 n). `fill' defaults to a
45 ;; procedure that raises an error, but you may supply a procedure that
46 ;; "fills in missing values".
47
48 (define/contract (in-take seq [n 2] [fill (make-fill 'in-take n)])
49 ((sequence?) (exact-positive-integer? fill/c) . ->* . sequence?)
4be1db3 @greghendershott Use `make-do-sequence', following examples in unstable/sequence.
authored
50 (make-do-sequence ;Model: unstable/sequence
51 (lambda ()
52 (define-values (more? get) (sequence-generate seq))
53 (values (lambda (_) ;pos->element
54 (apply values (for/list ([i n])
55 (cond [(more?) (get)]
56 [else (fill i)]))))
57 (lambda (_) #t) ;next-pos
58 #t ;initial-pos
59 (lambda (_) (more?)) ;continue-with-pos?
60 (lambda _ #t) ;continue-with-val?
61 (lambda _ #t) ;continue-after-pos+val?
62 ))))
0bec1f6 @greghendershott Add in-take-list sequence.
authored
63
64 (module+ test
f705155 @greghendershott Delete filter-take
authored
65 (require rackunit)
0bec1f6 @greghendershott Add in-take-list sequence.
authored
66 (test-case
5dd972c @greghendershott Generalize `X-take-list' functions to `X-take'.
authored
67 "in-take"
bd15932 @greghendershott Stream-based implementation.
authored
68 ;; Take from an empty sequence is '() (not an error)
69 (check-equal? (for/list ([(k v) (in-take (list))])
70 (cons k v))
71 '())
72 (check-equal? (for/list ([(k v) (in-take (vector))])
73 (cons k v))
74 '())
75 (check-equal? (for/list ([(k v) (in-take "")])
76 (cons k v))
77 '())
5dd972c @greghendershott Generalize `X-take-list' functions to `X-take'.
authored
78 ;; Sequence is multiple of take size
79 (check-equal? (for/list ([(k v) (in-take (list 'a "1" 'b "2"))])
80 (cons k v))
81 '([a . "1"][b . "2"]))
82 (check-equal? (for/list ([(k v) (in-take (vector 'a "1" 'b "2"))])
83 (cons k v))
84 '([a . "1"][b . "2"]))
85 (check-equal? (for/list ([(k v) (in-take "a1b2")])
86 (cons k v))
87 '([#\a . #\1][#\b . #\2]))
88 ;; Missing values raising an error (the default `fill')
89 (check-exn exn:fail?
90 (lambda () (for/list ([(k v) (in-take '(a "1" b "2" c) 2)])
91 (cons k v))))
92 ;; Missing values filled in
93 (check-equal? (for/list ([(k v) (in-take '(a "1" b "2" c) 2
94 (const "FILL"))])
95 (cons k v))
96 '([a . "1"][b . "2"][c . "FILL"]))
97 ;; Fill function is passed expected "index"
98 (check-equal? (for/list ([(a b c d e) (in-take '(0 1) 5 values)])
99 (list a b c d e))
100 '((0 1 2 3 4))) ;fill was called with 2 3 4
101 ;; Taking the same list with different take-sizes.
0bec1f6 @greghendershott Add in-take-list sequence.
authored
102 (define test-xs (list 0 1 2 3 4 5 6 7 8 9))
5dd972c @greghendershott Generalize `X-take-list' functions to `X-take'.
authored
103 (define (test-take n)
104 (for/list ([ts (in-values-sequence (in-take test-xs n))])
105 ts))
106 (check-equal? (test-take 1)
0bec1f6 @greghendershott Add in-take-list sequence.
authored
107 '((0) (1) (2) (3) (4) (5) (6) (7) (8) (9)))
5dd972c @greghendershott Generalize `X-take-list' functions to `X-take'.
authored
108 (check-equal? (test-take 2)
0bec1f6 @greghendershott Add in-take-list sequence.
authored
109 '((0 1) (2 3) (4 5) (6 7) (8 9)))
5dd972c @greghendershott Generalize `X-take-list' functions to `X-take'.
authored
110 (check-equal? (test-take 5)
0bec1f6 @greghendershott Add in-take-list sequence.
authored
111 '((0 1 2 3 4) (5 6 7 8 9)))))
Something went wrong with that request. Please try again.