3 API Documentation
3.1 Generic Collections and Sequences
Generic sequences are the bulk of this library, providing a uniform interface for interacting with collections. Sequences are distinct from Racket sequences, which are a different, much more ad-hoc concept.
A generic collection is any structure that can contain values, while a generic sequence represents a sequence of ordered values.
3.1.1 Collections
value
The following built-in datatypes have implementations for gen:collection:
immutable hash tables
immutable vectors
immutable hash sets
immutable dictionaries
procedure
(collection? v) → boolean?
v : any/c
3.1.1.1 Generic Methods
procedure
(conj coll item) → collection?
coll : collection? item : any/c
If extend is implemented but not conj, an implementation will automatically be provided.
> (conj '() 'a) '(a)
> (conj '(1 2) 3) '(3 1 2)
> (conj #(1 2) 3) '#(1 2 3)
> (conj (hash) '(a . b)) '#hash((a . b))
procedure
(extend a b) → collection?
a : collection? b : sequence?
If conj is implemented but not extend, an implementation will automatically be provided.
> (extend '(1 2) '(3 4)) '(4 3 1 2)
> (extend '() #(1 2 3 4)) '(4 3 2 1)
> (extend #() '(1 2 3 4)) '#(1 2 3 4)
> (extend (hash) (set '(a . b) '(b . c))) '#hash((b . c) (a . b))
3.1.1.2 Derived Functions
procedure
(conj* coll item ...) → collection?
coll : collection? item : any/c
> (conj* '() 1 2 3 4) '(4 3 2 1)
procedure
(extend* base extension ...) → collection?
base : collection? extension : sequence?
3.1.2 Sequences
value
The following built-in datatypes have implementations for gen:sequence:
immutable hash tables
immutable vectors
immutable hash sets
immutable dictionaries
3.1.2.1 Generic Methods
All implementations of gen:sequence are required to implement this method, unless they also implement gen:countable.
This method is optional if an implementation of nth is provided.
All implementations of gen:sequence are required to implement this method.
> (rest '(1 2 3)) '(2 3)
> (rest (set 'a 'b 'c)) (set 'b 'a)
> (rest (hash 'a 'b 'c 'd)) #<stream>
> (extend (hash) (rest (hash 'a 'b 'c 'd))) '#hash((a . b))
procedure
seq : sequence? index : exact-nonnegative-integer?
If seq also implements gen:countable and is known-finite?, bounds checking will automatically be provided, and a exn:fail:contract error will be raised if index is out of range.
This method is optional if an implementation of first is provided.
procedure
seq : sequence? index : exact-nonnegative-integer? value : any/c
> (set-nth '(1 2 3) 1 'b) '(1 b 3)
procedure
(update-nth seq index proc) → sequence?
seq : sequence? index : exact-nonnegative-integer? proc : (any/c . -> . any/c)
> (update-nth '(1 2 3) 1 (λ (n) (+ 10 n))) '(1 12 3)
procedure
seq : sequence? index : exact-nonnegative-integer? value : any/c
> (set-nth* '(1 2 3) 0 'a 2 'c) '(a 2 c)
procedure
(update-nth* seq index proc ... ...) → sequence?
seq : sequence? index : exact-nonnegative-integer? proc : (any/c . -> . any/c)
> (update-nth* '(1 2 3) 0 add1 2 sub1) '(2 2 2)
> (reverse '(1 2 3)) '(3 2 1)
> (reverse #(1 2 3)) #<random-access-sequence>
> (extend #() (reverse #(1 2 3))) '#(3 2 1)
procedure
(sequence->collection seq) → collection?
seq : sequence?
Beware that the fallback collection returned by this function can be very slow if repeatedly conj’d upon. However, since most sequences are also collections, it can also be much, much faster than copying the sequence into a collection type with extend. Therefore, it is recommended that general-purpose sequences which are not collections always implement a performant version of sequence->collection.
> (reverse #(2 1)) #<random-access-sequence>
> (collection? (reverse #(2 1))) #f
> (sequence->collection (reverse #(2 1))) #<appending-collection>
> (sequence->list (conj (sequence->collection (reverse #(2 1))) 3)) '(1 2 3)
procedure
(random-access? seq) → boolean?
seq : sequence?
This can be used as a heuristic to determine what sort of algorithm to use when operating over generic sequences. For example, if a sequence is determined to be random access, the default implementation for update-nth will use nth and set-nth. Otherwise, it will lazily loop.
3.1.2.2 Derived Functions
procedure
proc : procedure? arg : any/c args : sequence? kw-arg : any/c
> (apply + #(1 1 1)) 3
> (apply + (set 1 1 1)) 1
> (apply string-replace #:all? #f "foo" #("o" "a")) "fao"
In many cases, it may be preferably to use extend or extend*, which may also provide better performance, especially for homogenous sequence types.
> (append '(1 2) '(3 4)) #<stream>
> (sequence->list (append '(1 2) '(3 4))) '(1 2 3 4)
> (sequence->list (append (hash 'a 'b) (set 'c 'd))) '((a . b) c d)
procedure
seq : sequence? seqs : (sequenceof sequence?)
> (append* (stream '(1 2) '(3 4))) #<stream>
> (sequence->list (append* (stream '(1 2) '(3 4)))) '(1 2 3 4)
> (sequence->list (append* (stream (hash 'a 'b) (set 'c 'd)))) '((a . b) c d)
procedure
(build-sequence proc) → sequence?
proc : (exact-nonnegative-integer? . -> . any/c) (build-sequence n proc) → sequence? n : exact-nonnegative-integer? proc : (exact-nonnegative-integer? . -> . any/c)
By default, build-sequence produces an infinite sequence. If n is provided, then the result is limited to n elements; it is equivalent to (take n (build-sequence proc)).
> (sequence->list (build-sequence 5 values)) '(0 1 2 3 4)
> (sequence->list (subsequence (build-sequence sqr) 5 10)) '(25 36 49 64 81)
> (nth (cycle '(1 2 3)) 10) 2
> (sequence->list (take 5 (cycle '(a b)))) '(a b a b a)
procedure
start : exact-nonnegative-integer? = 0
procedure
end : number? (range start end [step]) → stream? start : number? end : number? step : number? = 1
procedure
(randoms [rand-gen])
→ (sequenceof (and/c real? inexact? (>/c 0) (</c 1)))
rand-gen : pseudo-random-generator? = (make-pseudo-random-generator) (randoms k [rand-gen]) → (sequenceof exact-nonnegative-integer?) k : (integer-in 1 4294967087)
rand-gen : pseudo-random-generator? = (make-pseudo-random-generator)
> (sequence->list (take 10 (randoms)))
'(0.8891172390283053
0.9662429825352833
0.08007691629603474
0.9205747632029353
0.908499310251292
0.37257213599397904
0.7778609059273891
0.3593700504277299
0.940237517601206
0.08499639287573513)
> (sequence->list (take 10 (randoms 20))) '(2 17 19 17 8 10 4 18 16 12)
procedure
n : exact-nonnegative-integer? seq : sequence?
> (sequence->list (take 10 (in-naturals))) '(0 1 2 3 4 5 6 7 8 9)
procedure
n : exact-nonnegative-integer? seq : sequence?
> (sequence->list (drop 5 (range 10))) '(5 6 7 8 9)
procedure
(subsequence seq start end) → sequence?
seq : sequence? start : exact-nonnegative-integer? end : exact-nonnegative-integer?
> (sequence->list (subsequence (in-naturals) 10 20)) '(10 11 12 13 14 15 16 17 18 19)
procedure
(subsequence* seq start len) → sequence?
seq : sequence? start : exact-nonnegative-integer? len : exact-nonnegative-integer?
> (sequence->list (subsequence* (in-naturals) 20 5)) '(20 21 22 23 24)
> (filter odd? '(1 2 3 4 5)) #<stream>
> (sequence->list (filter odd? '(1 2 3 4 5))) '(1 3 5)
procedure
proc : procedure? seq : sequence?
> (map add1 '(10 20 30)) #<stream>
> (sequence->list (map add1 '(10 20 30))) '(11 21 31)
> (sequence->list (map + '(5 10 15) #(3 6 9))) '(8 16 24)
> (define fibs (stream-cons 1 (stream-cons 1 (map + fibs (rest fibs)))))
> (sequence->list (take 15 fibs)) '(1 1 2 3 5 8 13 21 34 55 89 144 233 377 610)
procedure
proc : procedure? init : any/c seq : sequence?
Unlike base:foldl, the accumulator argument is always provided to proc first, not last.
> (foldl cons null (set 1 2 3 4)) '((((() . 1) . 3) . 2) . 4)
> (foldl (λ (a b) (cons b a)) null (set 1 2 3 4)) '(4 2 3 1)
procedure
(foldl/steps proc init seq ...+) → sequence?
proc : procedure? init : any/c seq : sequence?
> (sequence->list (foldl/steps + 0 '(1 3 7))) '(0 1 4 11)
> (sequence->list (foldl/steps conj #() '(a b c))) '(#() #(a) #(a b) #(a b c))
> (let ([factorials (foldl/steps * 1 (naturals 1))]) (nth factorials 6)) 720
procedure
proc : procedure? seq : sequence?
procedure
proc : procedure? seq : sequence?
procedure
proc : procedure? seq : sequence?
procedure
seq : (and/c sequence? (not/c empty?)) >? : (any/c any/c . -> . any/c) extract-key : (any/c . -> . any/c) = values
> (find-best '("pears" "bananas" "apples") string<?) "apples"
> (find-best '((3 pears) (1 banana) (2 apples)) string<? #:key (compose1 symbol->string second)) '(2 apples)
> (find-best '((2 apples) (5 apples)) string<? #:key (compose1 symbol->string second)) '(2 apples)
The functions find-min and find-max are defined in terms of find-best, with < and > as the ordering procedures, respectively.
> (find-min '((3 pears) (1 banana) (2 apples)) #:key first) '(1 banana)
> (find-min '((1 banana) (1 orange)) #:key first) '(1 banana)
> (find-max '((3 pears) (1 banana) (2 apples)) #:key first) '(3 pears)
> (find-max '((1 banana) (1 orange)) #:key first) '(1 banana)
> (index-of '(a b c) 'b) 1
> (index-of '(a b c) 'd) #f
> (index-of '(1 2 3) 2.0) #f
> (index-of '(1 2 3) 2.0 =) 1
> (index-where '(1 2 3) positive?) 0
> (index-where '(-1 2 3) positive?) 1
> (index-where '(-1 -2 -3) positive?) #f
> (flatten '((a) b (c (d) e) ())) #<stream>
> (sequence->list (flatten '((a) b (c (d) e) ()))) '(a b c d e)
> (sequence->list (flatten '((((()()()))(((()))()))))) '()
> (sixth (flatten (repeat 1))) 1
procedure
(indexed seq)
→ (sequenceof (cons/c exact-nonnegative-integer? any/c)) seq : sequence?
> (sequence->list (indexed '(a b c))) '((0 . a) (1 . b) (2 . c))
> (extend (hash) (indexed '(a b c))) '#hash((1 . b) (0 . a) (2 . c))
procedure
(chunk n seq) → (sequenceof sequence?)
n : exact-positive-integer? seq : sequence?
> (sequence->list* (chunk 3 (range 10))) '((0 1 2) (3 4 5) (6 7 8) (9))
procedure
(chunk* n seq) → (sequenceof sequence?)
n : exact-positive-integer? seq : sequence?
> (sequence->list* (chunk* 3 (range 10))) rest: contract violation
expected: (not/c empty?)
given: #<stream>
in: an and/c case of
the 1st argument of
(-> (and/c sequence? (not/c empty?)) any)
contract from:
<pkgs>/collections/data/collection/collection.rkt
blaming: <pkgs>/collections/data/collection/sequence.rkt
(assuming the contract is correct)
at: <pkgs>/collections/data/collection/collection.rkt:34.3
procedure
(append-map f seq ...+) → sequence?
f : procedure? seq : sequence?
> (sequence->list (append-map values '((1) (2) (3)))) '(1 2 3)
procedure
(cartesian-product seq ...) → (sequenceof sequence?)
seq : sequence?
> (sequence->list* (cartesian-product '(1 2) '(a b) '(c d))) '((1 a c) (1 a d) (1 b c) (1 b d) (2 a c) (2 a d) (2 b c) (2 b d))
> (sequence->list* (cartesian-product '(a) '(1 2 3))) '((a 1) (a 2) (a 3))
> (sequence->list* (cartesian-product '(4 5 6) '(d e f) '(#t #f)))
'((4 d #t)
(4 d #f)
(4 e #t)
(4 e #f)
(4 f #t)
(4 f #f)
(5 d #t)
(5 d #f)
(5 e #t)
(5 e #f)
(5 f #t)
(5 f #f)
(6 d #t)
(6 d #f)
(6 e #t)
(6 e #f)
(6 f #t)
(6 f #f))
procedure
seq : sequence?
procedure
seq : sequence?
procedure
seq : sequence?
procedure
seq : sequence?
procedure
seq : sequence?
procedure
seq : sequence?
procedure
seq : sequence?
procedure
seq : sequence?
procedure
seq : sequence?
> (second (in-naturals)) 1
> (third (in-naturals)) 2
> (fourth (in-naturals)) 3
syntax
(for/sequence (for-clause ...) body-or-break ... body)
syntax
(for*/sequence (for-clause ...) body-or-break ... body)
The for*/sequence form is the same as for/sequence but with the implicit nesting behavior of for*.
> (extend (set) (for/sequence ([i (in-range 10)]) (* i i))) (set 16 1 9 0 81 64 49 4 36 25)
syntax
(for/sequence/derived name-id (for-clause ...) body-or-break ... body)
syntax
(for*/sequence/derived name-id (for-clause ...) body-or-break ... body)
> (define-syntax-rule (for/immutable-vector . rest) (extend #() (for/sequence/derived for/immutable-vector . rest)))
> (for/immutable-vector ([i (in-range 10)]) (* i i)) '#(0 1 4 9 16 25 36 49 64 81)
> (for/immutable-vector (malformed) (* i i)) eval:3:0: for/immutable-vector: bad sequence binding clause
at: malformed
in: (for/immutable-vector (malformed) (* i i))
procedure
(sequence->list seq) → list?
seq : sequence?
If seq is infinite, then this function will not terminate, and it will infinitely allocate memory until it is exhausted.
> (sequence->list #(1 2 3)) '(1 2 3)
> (sequence->list (hash 'a 'b 1 2 "foo" "bar")) '((1 . 2) ("foo" . "bar") (a . b))
procedure
(sequence->list* seq) → list?
seq : sequence?
If seq or any of its subsequences are infinite, then this function will not terminate, and it will infinitely allocate memory until it is exhausted.
> (sequence->list* #(1 #(2 3))) '(1 (2 3))
> (sequence->list* (chunk 2 (range 10))) '((0 1) (2 3) (4 5) (6 7) (8 9))
procedure
(sequence->string seq) → (and/c string? sequence?)
seq : (sequenceof char?)
procedure
(sequence->bytes seq) → (and/c bytes? sequence?)
seq : (sequenceof byte?)
procedure
(generate-sequence gen) → sequence?
gen : generator?
> (sequence->list (take 5 (generate-sequence (generator () (let loop ([n 0]) (yield (* n n)) (loop (add1 (* n n)))))))) '(0 1 4 25 676)
3.2 General-Purpose Interfaces
3.2.1 Countable Collections
Lots of data structures may be considered countable—that is, they have a discrete number of elements. The gen:countable interface only provides a single function, length.
value
The following built-in datatypes have implementations for gen:countable:
For streams, if the argument is infinite, then length does not terminate.
> (length (range 20)) 20
> (length #(λ)) 1
> (length "Hello!") 6
> (length (set 1 2 3 4 5)) 5
> (struct wrapped-collection (value) #:methods gen:countable [(define/generic -length length) (define (length w) (-length (wrapped-collection-value w)))])
> (length (wrapped-collection (hash 'a "b" 'c "d"))) 2
procedure
(countable? v) → boolean?
v : any/c
procedure
(length countable) → exact-nonnegative-integer?
countable : countable?
procedure
(known-finite? countable) → boolean?
countable : countable?
If no implementation for known-finite? is provided, it will always return #f.
> (known-finite? #(a b c)) #t
> (known-finite? (sequence->list (range 10))) #t
> (known-finite? (in-naturals)) #f
3.2.2 Indexable Collections
Data structures are indexable if they provide any sort of indexed data.
value
Be careful when using ref with generic sequences. If numeric indexing is your intention, use nth instead, since ref and nth may have different behaviors on the same sequence. Notably, ref on association lists uses dict-ref, not list-ref.
All generic sequences are also indexable, so implementations of gen:sequence do not need to implement gen:indexable if they provide simply key/value mappings based on index. Additionally, mutable hash tables, mutable vectors, and dictionaries are also indexable.
> (ref '(a b c) 1) 'b
> (ref (hash 'foo "bar") 'foo) "bar"
> (ref '((1 . 2) (3 . 4)) 1) 2
> (set-ref '(a b c) 1 'x) '(a x c)
> (set-ref (hash 'foo "bar") 'foo "baz") '#hash((foo . "baz"))
> (set-ref '((1 . 2) (3 . 4)) 1 -2) '((1 . -2) (3 . 4))
procedure
(indexable? v) → boolean?
v : any/c
procedure
collection : indexable? index : any/c
procedure
collection : indexable? index : any/c value : any/c
3.2.3 Using sequences with match
If pat ... splicing patterns are used in a non-final position, the sequence will be forced, and if the sequence is not finite, the match will not terminate. Otherwise, the other elements of the sequence not matched will not be forced, including a possible lazy tail.
> (match (stream 1 2 3 4) [(sequence a b c d) c]) 3
> (match (stream 1 2 3 4) [(sequence a b ... c) b]) '(2 3)
> (match (stream 1 2 3 4) [(sequence a b ...) b]) #<stream>
3.2.4 Contracts on Collections
procedure
(sequenceof ctc [#:chaperone? chaperone?]) → contract?
ctc : contract? chaperone? : any/c = #f
If chaperone? is non-#f, then the result will always be a chaperone-contract?, and ctc must also be a chaperone-contract?. If chaperone? is #f, the result will always be a simple contract?.
For most sequence types, when a sequenceof contract is applied to a sequence, the result is always equal? to its input. However, for a small set of sequences, such as hash tables, strings, and byte strings, the result will be an entirely disparate type of sequence. This behavior is only supported for non-chaperone contracts, so if chaperone? is non-#f, then those sequences will not be permitted by the contract.