Seq:   A Sequence Library
1 Modules
2 Naming Conventions
2.1 Verbs
2.2 Suffixes
3 APIs
3.1 Index and Length-based
by
rest
init
take
prefix
suffix
drop
suffix-at
infix
infix-at
index-of
index
index-where
nth
set-nth
remove-at
truncate
3.2 Specific Element
find
remove
3.3 Filtering
filter
take-when
drop-when
take-while
drop-while
take-until
drop-until
deduplicate
trim-if
trim
trim-by
3.4 Infixes
cut-when
cut
cut-at
cut-where
cut-by
cut-with
find-infix
replace-infix
3.5 Variations
suffixes
prefixes
infixes
3.6 Predicates
exists
for-all
prefix?
starts-with?
suffix?
ends-with?
infix?
contains?
3.7 Defining
multiples
powers
iterate
intersperse
add-between
wrap-each
join-with
weave
range
map
3.8 Composing
zip
zip-with
unzip
unzip-with
interleave
choose
3.9 Permuting
rotate-left
rotate-right
rotate
rotations
reverse
8.12

Seq: A Sequence Library🔗ℹ

Siddhartha Kasivajhula

 (require seq) package: seq-lib

Standard and general-purpose sequence utilities.

This library builds on top of the Generic Collections foundation to provide a broad range of general-purpose utilities that work on all sequences. It also leverages Generic Relations to provide an optional "isomorphic" layer that ensures symmetry of input and output types while using these interfaces.

Some of these interfaces are either implementations of or are inspired by the Scheme specifications for list utilities, while others are similar in spirit. An attempt has been made to adhere to intuitive naming conventions and categories to minimize the need to lookup documentation and support the ability to guess the name of an unknown function rather than learn these (numerous) names purely through familiarity.

    1 Modules

    2 Naming Conventions

      2.1 Verbs

      2.2 Suffixes

    3 APIs

      3.1 Index and Length-based

      3.2 Specific Element

      3.3 Filtering

      3.4 Infixes

      3.5 Variations

      3.6 Predicates

      3.7 Defining

      3.8 Composing

      3.9 Permuting

1 Modules🔗ℹ

 (require seq/base) package: seq-lib

The core set of APIs, exhibiting functional, generic, and lazy semantics (like the interfaces in data/collection). This is an internal module and generally should not be used directly.

 (require seq/api) package: seq-lib

The default module that is imported via (require seq). It simply annotates the interfaces in seq/base, as well as a few from data/collection, with known finiteness information, which may be leveraged by applications for whatever purpose. In particular, this information is used in the seq/iso layer to determine whether preserving type symmetry is possible. Once again, this is an internal module and should not be used directly – just implicitly via (require seq).

 (require seq/iso) package: seq-lib

This module provides all of the APIs from seq/api in "isomorphic" form, so that output types will match input types where it makes sense. In general, therefore, the interfaces in this module are not lazy.

In particular, output types will match input types for all finite sequences that are either (a) a known built-in type such as a list, or (b) a custom type implementing both gen:collection as well as gen:appendable (in addition to gen:sequence). Note that in order for these isomorphic APIs to function correctly for custom types, the implementation of extend for gen:collection in the custom type must preserve the order of elements (for example, unlike the default implementation for lists, which reverses the order of elements).

2 Naming Conventions🔗ℹ

These naming conventions are intended to minimize the need to look up documentation while working with sequences.

While some of the provided sequence utilities have standard names familiar from string or list contexts, others take their name from the Scheme specification or are borrowed from other functional languages such as Haskell. When the utilities don’t take their name from one of these sources, they instead have a formulaic name that is approximately expressed as:

 

expr

 ::= 

verb phrase

 

  |  

special phrase

 

verb phrase

 ::= 

( verb args* noun )

 

  |  

( verb - modifier args* noun )

 

verb

 ::= 

take

 

  |  

drop

 

  |  

prefix

 

  |  

suffix

 

  |  

cut

 

  |  

trim

 

  |  

find

 

  |  

remove

 

  |  

choose

 

  |  

index

 

  |  

zip

 

  |  

unzip

 

modifier

 ::= 

while

 

  |  

until

 

  |  

when

 

  |  

if

 

  |  

unless

 

  |  

at

 

  |  

where

 

  |  

with

 

  |  

by

 

special phrase

 ::= 

( special operation args* noun )

 

special operation

 ::= 

by

 

  |  

exists

 

  |  

for-all

 

  |  

join-with

 

  |  

intersperse

 

  |  

add-between

 

  |  

wrap-each

 

  |  

interleave

 

  |  

choose

 

  |  

deduplicate

 

  |  

prefix?

 

  |  

starts-with?

 

  |  

suffix?

 

  |  

ends-with?

 

  |  

infix?

 

  |  

contains?

 

  |  

prefix

 

  |  

suffix

 

  |  

suffix-at

 

  |  

prefixes

 

  |  

suffixes

 

  |  

infixes

 

  |  

weave

 

  |  

rotate-left

 

  |  

rotate-right

 

  |  

multiples

 

  |  

powers

 

args

 ::= 

any parameters for the operation to be performed

 

noun

 ::= 

sequence?

Whenever a formulaic name is used for a well-known interface, the more common name is also usually provided as an alias. Not every interface here corresponds neatly to a naming convention, but in cases where they do, verbs and suffixes have the following meanings:

2.1 Verbs🔗ℹ

2.2 Suffixes🔗ℹ

3 APIs🔗ℹ

The API documentation here applies to all of the seq modules, but the examples use seq/iso for convenience.

3.1 Index and Length-based🔗ℹ

Reason in terms of gestalt properties of sequences, such as index and length, as opposed to their contents.

procedure

(by n seq)  sequence?

  n : exact-nonnegative-integer?
  seq : sequence?
A sequence containing every n’th element in the input sequence.

Examples:
> (->list (take 10 (by 5 (naturals))))

'(0 5 10 15 20 25 30 35 40 45)

> (->list (take 10 (by 2 (naturals))))

'(0 2 4 6 8 10 12 14 16 18)

> (->list (take 10 (by 2 (naturals 1))))

'(1 3 5 7 9 11 13 15 17 19)

> (->list (take 10 (by 7 (drop 100 (naturals)))))

'(100 107 114 121 128 135 142 149 156 163)

> (->list (by 3 (subsequence (naturals) 10 20)))

'(10 13 16 19)

> (->list (by 3 #(1 2 3 4 5 6 7 8 9 10)))

'(1 4 7 10)

procedure

(rest seq)  sequence?

  seq : sequence?
Identical to rest from data/collection, except that it includes additional runtime annotations to support isomorphic behavior.

Examples:
> (rest (list 1 2 3))

'(2 3)

> (rest "apple")

"pple"

> (->list (take 5 (rest (naturals))))

'(1 2 3 4 5)

procedure

(init seq)  sequence?

  seq : sequence?
The opposite of rest, this produces a sequence containing all of the elements of seq except the last one. Like rest, this sequence is lazily generated.

Examples:
> (init (list 1 2 3))

'(1 2)

> (init "apple")

"appl"

> (->list (take 5 (init (naturals))))

'(0 1 2 3 4)

procedure

(take n seq)  sequence?

  n : exact-nonnegative-integer?
  seq : sequence?

procedure

(prefix n seq)  sequence?

  n : exact-nonnegative-integer?
  seq : sequence?

procedure

(suffix n seq)  sequence?

  n : exact-nonnegative-integer?
  seq : sequence?

procedure

(drop n seq)  sequence?

  n : exact-nonnegative-integer?
  seq : sequence?

procedure

(suffix-at n seq)  sequence?

  n : exact-nonnegative-integer?
  seq : sequence?
prefix returns the first n elements of seq, i.e. a prefix of length n; it is an alias for take. suffix analogously returns the last n elements of seq, i.e. a suffix of length n. suffix-at is an alias for drop, returning the suffix at the index n. take and drop are identical to take and drop from data/collection except that they include additional runtime annotations to support isomorphic behavior.

Examples:
> (prefix 2 "apricot")

"ap"

> (suffix 2 "apricot")

"ot"

> (suffix-at 2 "apricot")

"ricot"

> (~ (prefix 2 "apricot") (suffix-at 2 "apricot"))

"apricot"

> (prefix 2 (list "banana" "apple" "apricot" "cherry" "avocado"))

'("banana" "apple")

> (suffix 3 (list 1 2 3 4 5 6 7 8 9))

'(7 8 9)

procedure

(infix start length seq)  sequence?

  start : exact-nonnegative-integer?
  length : exact-nonnegative-integer?
  seq : sequence?

procedure

(infix-at start end seq)  sequence?

  start : exact-nonnegative-integer?
  end : exact-nonnegative-integer?
  seq : sequence?
infix and infix-at compose prefix and suffix to extract subsequences of seq. infix expects a start index along with the length of the subsequence to extract, while infix-at expects start and end positions identifying the subsequence to be extracted. infix is essentially an alias for subsequence*, while infix-at is an alias for subsequence.

Examples:
> (infix 4 5 "the quick brown fox")

"quick"

> (infix-at 4 9 "the quick brown fox")

"quick"

> (infix 10 5 "the quick brown fox")

"brown"

> (infix-at 10 15 "the quick brown fox")

"brown"

> (->list (infix 64 5 (range 100)))

'(64 65 66 67 68)

> (->list (infix-at 64 69 (range 100)))

'(64 65 66 67 68)

procedure

(index-of [#:key key] elem seq)  exact-nonnegative-integer?

  key : procedure? = #f
  elem : any/c
  seq : sequence?

procedure

(index [#:key key] elem seq)  exact-nonnegative-integer?

  key : procedure? = #f
  elem : any/c
  seq : sequence?
The index of elem in seq, or #f if it doesn’t exist. If key is provided, it is used in the = comparison to find elem. index is an alias of index-of.

Examples:
> (index-of 3 (list 1 2 3 4 5))

2

> (index-of #:key string-upcase "cherry" (list "Apple" "CHERry" "BaNaNa"))

1

> (index-of " " "The quick brown fox")

3

procedure

(index-where pred seq ...)  sequence?

  pred : procedure?
  seq : sequence?
The first index where pred is true. The predicate pred must accept as many arguments as the number of input sequences seq.

Examples:
> (index-where positive? (list -3 -2 -1 0 1 2 3))

4

> (index-where > (list 1 2 3 4) (list 2 3 1 4))

2

procedure

(nth pos seq)  sequence?

  pos : natural-number/c
  seq : sequence?
Similar to nth but also supports negative indexing to count backwards from the end of the sequence.

Examples:
> (nth 3 (list 1 2 3 4 5))

4

> (nth 4 "The quick brown fox")

#\q

> (nth -1 (list "apple" "cherry" "banana"))

"banana"

procedure

(set-nth pos new-elt seq)  sequence?

  pos : natural-number/c
  new-elt : any/c
  seq : sequence?
Similar to set-nth but also supports negative indexing to count backwards from the end of the sequence.

Examples:
> (set-nth 3 0 (list 1 2 3 4 5))

'(1 2 3 0 5)

> (set-nth 4 "Q" "The quick brown fox")

"The Quick brown fox"

> (set-nth -1 "durian" (list "apple" "cherry" "banana"))

'("apple" "cherry" "durian")

procedure

(remove-at pos seq)  sequence?

  pos : natural-number/c
  seq : sequence?
Remove the element at index pos from seq.

Examples:
> (remove-at 3 (list 1 2 3 4 5))

'(1 2 3 5)

> (remove-at 3 "The quick brown fox")

"Thequick brown fox"

> (remove-at 1 (list "apple" "cherry" "banana"))

'("apple" "banana")

procedure

(truncate seq ref-seq)  sequence?

  seq : sequence?
  ref-seq : sequence?
Truncate seq so that its length does not exceed that of ref-seq. Equivalent to (zip-with (arg 0)).

Examples:
> (truncate "I wandered lonely as a cloud." "Max. tweet length.")

"I wandered lonely "

> (truncate "Nevermore." "Max. tweet length.")

"Nevermore."

> (->list (truncate (repeat "apple") "apple"))

'("apple" "apple" "apple" "apple" "apple")

> (->string (truncate (drop 2 (cycle "apple")) "apple"))

"pleap"

3.2 Specific Element🔗ℹ

Refer to and reason in terms of specific elements contained in sequences.

procedure

(find pred seq ...)  any/c

  pred : procedure?
  seq : sequence?
Find the first element in seq that satisfies pred. If no item satisfies the predicate, then the result is #f. If more than one sequence is provided, pred must accept as many arguments as there are sequences, and the result is a list of the first elements that satisfy it.

Examples:
> (find number? (list "cherry" 'banana 10 30))

10

> (find positive? (list -1 -2 -1 2 3))

2

> (find (curry prefix? "ap") (list "banana" "apple" "apricot"))

"apple"

> (find > (list -1 -2 -1 2 3) (list -1 3 1 0 1))

'(2 0)

procedure

(remove [#:key key    
  #:how-many how-many]    
  elem    
  seq)  exact-nonnegative-integer?
  key : procedure? = #f
  how-many : exact-nonnegative-integer? = #f
  elem : any/c
  seq : sequence?
Remove occurrences of elem from seq. If how-many is not specified, then all occurrences are removed. The key argument, if provided, is passed through to the underlying generic equality relation, = used to identify occurrences of elem.

Examples:
> (remove 3 (list 1 2 3 4 5))

'(1 2 4 5)

> (remove " " "The quick brown fox")

"Thequickbrownfox"

> (remove #:key string-upcase "cherry" (list "Apple" "CHERry" "BaNaNa"))

'("Apple" "BaNaNa")

> (->list (remove #:key (curryr remainder 3) 1 #:how-many 2 (range 10)))

'(0 2 3 5 6 7 8 9)

3.3 Filtering🔗ℹ

Extract a subsequence.

procedure

(filter pred seq)  sequence?

  pred : procedure?
  seq : sequence?

procedure

(take-when pred seq)  sequence?

  pred : procedure?
  seq : sequence?

procedure

(drop-when pred seq)  sequence?

  pred : procedure?
  seq : sequence?
An alias for filter, take-when selects all elements from seq that satisfy pred, while drop-when selects those elements that do not satisfy pred. filter and take-when are identical to filter from data/collection, except that they include additional runtime annotations to support isomorphic behavior.

Examples:
> (filter positive? #(1 -2 3))

'#(1 3)

> (take-when positive? (list 1 -4 -1 3))

'(1 3)

> (drop-when positive? (list 1 -4 -1 3))

'(-4 -1)

> (take-when (curry prefix? "ap") (list "banana" "apple" "apricot" "cherry"))

'("apple" "apricot")

> (drop-when char-whitespace? "  the quick   \tbrown\nfox")

"thequickbrownfox"

procedure

(take-while pred seq)  sequence?

  pred : procedure?
  seq : sequence?

procedure

(drop-while pred seq)  sequence?

  pred : procedure?
  seq : sequence?
Select (take) or exclude (drop) elements from seq as long as they satisfy pred, stopping at the first one that fails to.

Examples:
> (take-while positive? (list 1 2 -4 -12 3))

'(1 2)

> (drop-while positive? (list 1 2 -4 -12 3))

'(-4 -12 3)

> (take-while positive? (list -1 3 2 4 -12))

'()

> (drop-while positive? (list -1 3 2 4 -12))

'(-1 3 2 4 -12)

> (take-while (curry prefix? "ap") (list "apple" "banana" "apricot" "cherry"))

'("apple")

> (drop-while (curry prefix? "ap") (list "apple" "banana" "apricot" "cherry"))

'("banana" "apricot" "cherry")

procedure

(take-until pred seq)  sequence?

  pred : procedure?
  seq : sequence?

procedure

(drop-until pred seq)  sequence?

  pred : procedure?
  seq : sequence?
Select (take) or exclude (drop) elements from seq as long as they do not satisfy pred, stopping at the first one that succeeds.

Examples:
> (take-until positive? (list -1 -2 3 2 -4))

'(-1 -2)

> (drop-until positive? (list -1 -2 3 2 -4))

'(3 2 -4)

> (take-until positive? (list 1 3 2 -4))

'()

> (drop-until positive? (list 1 3 2 -4))

'(1 3 2 -4)

> (take-until (curry prefix? "ap") (list "banana" "apple" "apricot" "cherry"))

'("banana")

> (drop-until (curry prefix? "ap") (list "banana" "apple" "apricot" "cherry"))

'("apple" "apricot" "cherry")

procedure

(deduplicate [#:key key] seq)  list?

  key : (-> any/c any/c) = #f
  seq : sequence?
Remove duplicate occurrences of elements in seq, using the generic equivalence relation = to determine equality.

Examples:
> (deduplicate (list 1 2 3 "hi" 3 "bye" 1 2 "hi"))

'(1 2 3 "hi" "bye")

> (deduplicate #:key string-upcase (list "hi" "hello" "HI" "HeLLo"))

'("hi" "hello")

procedure

(trim-if pred    
  seq    
  [#:side side    
  #:how-many how-many])  sequence?
  pred : procedure?
  seq : sequence?
  side : (one-of/c 'left 'right 'both) = 'both
  how-many : exact-nonnegative-integer? = #f
Trim elements on one or both sides of seq as long as they satisfy pred, terminating at the first element that does not.

Examples:
> (trim-if negative? (list -1 0 1 2 3 -2 -1))

'(0 1 2 3)

> (trim-if char-whitespace? "  \t the quick brown fox\n ")

"the quick brown fox"

procedure

(trim elem    
  seq    
  [#:side side    
  #:how-many how-many])  sequence?
  elem : any/c
  seq : sequence?
  side : (one-of/c 'left 'right 'both) = 'both
  how-many : exact-nonnegative-integer? = #f
Trim occurrences of elem (under the equivalence check =) on one or both sides of seq, terminating at the first element that is not = to elem.

Examples:
> (trim -1 (list -1 0 1 2 3 -2 -1))

'(0 1 2 3 -2)

> (trim " " "  \t the quick brown fox\n ")

"\t the quick brown fox\n"

procedure

(trim-by left right seq)  sequence?

  left : exact-nonnegative-integer?
  right : exact-nonnegative-integer?
  seq : sequence?
Trim seq on the left by left elements, and on the right by right elements.

Examples:
> (trim-by 1 2 (list -1 0 1 2 3 -2 -1))

'(0 1 2 3)

> (trim-by 4 5 "the quick brown fox\n")

"quick brown"

3.4 Infixes🔗ℹ

Refer to and reason in terms of contiguous subsequences, or "infixes."

procedure

(cut-when pred seq)  sequence?

  pred : procedure?
  seq : sequence?
Cut a subsequence from seq at each point where pred is satisfied.

Examples:
> (->list (cut-when (curry = #\space) "hello there old friend"))

'("hello" "there" "old" "friend")

> (->list (cut-when negative? (list -1 4 1 -3 2 -5 3 7)))

'(() (4 1) (2) (3 7))

procedure

(cut elem seq [#:key key])  sequence?

  elem : any/c
  seq : sequence?
  key : procedure? = #f
Similar to string-split but generalized to work on any sequence, this cuts a subsequence from seq at each point where elem is encountered, excluding elem. In some contexts this operation is called "tokenization."

In the special case where the input sequence is a string, elem may be either a character or a string representing a character. If the "trim" behavior of string-split is desired, just trim or trim-if (e.g. using char-whitespace?) the sequence prior to calling cut. The key argument, if provided, is passed through to the underlying generic equality relation, =.

Examples:
> (->list (cut " " "hello there old friend"))

'("hello" "there" "old" "friend")

> (->list (cut 1 (list -1 4 1 -3 2 -5 1 3 7)))

'((-1 4) (-3 2 -5) (3 7))

procedure

(cut-at pos seq)  
sequence? sequence?
  pos : exact-nonnegative-integer?
  seq : sequence?
Cut seq at the index pos, resulting in a pair of subsequences.

Examples:
> (define-values (before after) (cut-at 11 "hello there old friend"))
> (list before after)

'("hello there" " old friend")

> (define-values (before after) (cut-at 3 (list -1 4 1 -3 2 -5 3 7)))
> (list before after)

'((-1 4 1) (-3 2 -5 3 7))

procedure

(cut-where pred seq)  
sequence? sequence?
  pred : (-> any/c boolean?)
  seq : sequence?
Cut seq at the (first) point where pred returns true, resulting in a pair of subsequences.

Examples:
> (define-values (before after) (cut-where char-whitespace? "hello there old friend"))
> (list before after)

'("hello" " there old friend")

> (define-values (before after) (cut-where positive? (list -2 -1 0 1 2 3 4)))
> (list before after)

'((-2 -1 0) (1 2 3 4))

procedure

(cut-by n seq)  
sequence? sequence?
  n : (-> any/c boolean?)
  seq : sequence?
Cut seq into subsequences of length n.

Examples:
> (->list (cut-by 5 "hello there old friend"))

'("hello" " ther" "e old" " frie")

> (->list (cut-by 3 (list -2 4 1 -3 2 -5 3 7)))

'((-2 4 1) (-3 2 -5))

procedure

(cut-with pred seq)  
sequence? sequence?
  pred : (-> any/c boolean?)
  seq : sequence?
Similar to partition, use a predicate pred to cut seq into two subsequences, one containing those elements for which pred holds, and the other containing those elements of seq for which pred fails.

Examples:
> (define-values (yes no)
                 (cut-with (curry prefix? "ap")
                           (list "banana" "apple" "apricot" "cherry")))
> (list yes no)

'(("apple" "apricot") ("banana" "cherry"))

> (define-values (yes no)
                 (cut-with positive?
                           (list -2 4 1 -3 2 -5 3 7)))
> (list yes no)

'((4 1 2 3 7) (-2 -3 -5))

procedure

(find-infix [#:key key] nfx seq)  any/c

  key : (-> any/c any/c) = #f
  nfx : sequence?
  seq : sequence?
Finds the first occurrence of the subsequence nfx in seq and returns its index, if present, or #f otherwise. The infixes are compared using the generic = relation, and any provided key for comparison is forwarded to it.

Examples:
> (find-infix "fox" "the quick brown fox jumps over the lazy dog")

16

> (find-infix (range 3 5) (range 10))

3

> (find-infix "fish" "the quick brown fox jumps over the lazy dog")

#f

procedure

(replace-infix [#:key key    
  #:how-many how-many]    
  nfx    
  new-nfx    
  seq)  any/c
  key : (-> any/c any/c) = #f
  how-many : exact-nonnegative-integer? = #f
  nfx : sequence?
  new-nfx : sequence?
  seq : sequence?
Replaces the first how-many occurrences of the subsequence nfx in seq with new-nfx. The infixes are compared using the generic = relation, and any provided key for comparison is forwarded to it. If how-many is not specified, all occurrences are replaced.

Examples:
> (replace-infix "fox" "bear" "the quick brown fox jumps over the lazy dog")

"the quick brown bear jumps over the lazy dog"

> (->list (replace-infix (range 3 5) (range 13 21 2) (range 10)))

'(0 1 2 13 15 17 19 5 6 7 8 9)

3.5 Variations🔗ℹ

Derive sequences from an existing sequence.

procedure

(suffixes seq)  sequence?

  seq : sequence?
A sequence of all suffixes or "tails" of seq.

Examples:
> (->list (suffixes (list 1 2 3 4 5)))

'((1 2 3 4 5) (2 3 4 5) (3 4 5) (4 5) (5) ())

> (->list (suffixes "echo"))

'("echo" "cho" "ho" "o" "")

> (define (fibs)
    (stream-cons 1
      (stream-cons 1
        (apply zip-with + (take 2 (suffixes (fibs)))))))
> (->list (take 10 (fibs)))

'(1 1 2 3 5 8 13 21 34 55)

procedure

(prefixes seq)  sequence?

  seq : sequence?
A sequence of all prefixes of seq.

Examples:
> (->list (prefixes "wild west"))

'("" "w" "wi" "wil" "wild" "wild " "wild w" "wild we" "wild wes" "wild west")

> (->list (take 5 (map ->list (prefixes (naturals)))))

'(() (0) (0 1) (0 1 2) (0 1 2 3))

procedure

(infixes len seq)  sequence?

  len : exact-positive-integer?
  seq : sequence?
A sequence of all infixes of seq of length len.

Examples:
> (->list (infixes 4 "avocado"))

'("avoc" "voca" "ocad" "cado")

> (->list (take 5 (infixes 3 (naturals))))

'((0 1 2) (1 2 3) (2 3 4) (3 4 5) (4 5 6))

3.6 Predicates🔗ℹ

Assert or deny properties of sequences.

procedure

(exists pred seq ...)  boolean?

  pred : (-> any/c boolean?)
  seq : sequence?
Similar to exists but generalized to all sequences rather than only lists, this checks if any of the sequence values fulfill a provided predicate. pred must accept a number of arguments equal to the number of provided sequences seq. This is an alias for ormap.

Examples:
> (exists positive? (list -1 -3 0 -2 -5))

#f

> (exists positive? (list -1 -3 0 2 -5))

#t

> (exists < (list 1 2 3 4 5) (list 1 0 2 2 7))

#t

procedure

(for-all pred seq ...)  boolean?

  pred : (-> any/c boolean?)
  seq : sequence?
Similar to for-all but generalized to all sequences rather than only lists, this checks if all of the sequence values fulfill a provided predicate. pred must accept a number of arguments equal to the number of provided sequences seq. This is an alias for andmap.

Examples:
> (for-all positive? (list -1 3 0 2 5))

#f

> (for-all positive? (list 1 3 2 2 5))

#t

> (for-all < (list 1 2 3 4 5) (list 2 3 4 5 6))

#t

procedure

(prefix? nfx seq ...)  boolean?

  nfx : sequence?
  seq : sequence?

procedure

(starts-with? nfx seq ...)  boolean?

  nfx : sequence?
  seq : sequence?

procedure

(suffix? nfx seq ...)  boolean?

  nfx : sequence?
  seq : sequence?

procedure

(ends-with? nfx seq ...)  boolean?

  nfx : sequence?
  seq : sequence?

procedure

(infix? nfx seq ...)  boolean?

  nfx : sequence?
  seq : sequence?

procedure

(contains? nfx seq ...)  boolean?

  nfx : sequence?
  seq : sequence?
prefix? / starts-with? checks if the sequence seq contains nfx at its head. suffix? / ends-with? checks if the sequence seq contains nfx at its tail end. infix? / contains? checks if the sequence seq contains nfx.

Examples:
> (prefix? (list 1 3) (list 1 3 0 2 5))

#t

> (prefix? "ap" "apricot")

#t

> (suffix? (list 2 5) (list 1 3 0 2 5))

#t

> (suffix? "cot" "apricot")

#t

> (infix? (list 3 0) (list 1 3 0 2 5))

#t

> (infix? "rico" "apricot")

#t

3.7 Defining🔗ℹ

Construct new sequences from primitive elements and other sequences. Not to be confused with composing sequences.

procedure

(multiples elem [n])  sequence?

  elem : any/c
  n : natural-number/c = 0
A sequence of all multiples of elem starting at the n’th one.

Examples:
> (->list (take 10 (multiples 3)))

'(0 3 6 9 12 15 18 21 24 27)

> (->list (take 10 (multiples 3 1)))

'(3 6 9 12 15 18 21 24 27 30)

> (->list (take 10 (map add1 (multiples 3))))

'(1 4 7 10 13 16 19 22 25 28)

procedure

(powers elem [op])  sequence?

  elem : any/c
  op : (one-of/c ~ + *) = ~
A sequence of all powers of elem under the operation op.

Examples:
> (->list (take 10 (powers 3)))

'(0 3 6 9 12 15 18 21 24 27)

> (->list (take 10 (powers 3 *)))

'(1 3 9 27 81 243 729 2187 6561 19683)

> (->list (take 4 (powers "abc")))

'("" "abc" "abcabc" "abcabcabc")

> (->list (take 4 (powers '(1 2 3))))

'(() (1 2 3) (1 2 3 1 2 3) (1 2 3 1 2 3 1 2 3))

> (->list (take 10 (onto (powers add1) 0)))

'(0 1 2 3 4 5 6 7 8 9)

> (define (double x) (* 2 x))
> (->list (take 10 (onto (powers double) 2)))

'(2 4 8 16 32 64 128 256 512 1024)

procedure

(iterate f elem)  sequence?

  f : procedure?
  elem : any/c
A sequence obtained by repeated application of f, starting with the seed value elem.

Examples:
> (->list (take 10 (iterate add1 3)))

'(3 4 5 6 7 8 9 10 11 12)

> (->list (take 10 (iterate (power add1 2) 3)))

'(3 5 7 9 11 13 15 17 19 21)

> (->list (take 5 (iterate sqr 2)))

'(2 4 16 256 65536)

> (define (double x) (* 2 x))
> (->list (take 10 (iterate double 2)))

'(2 4 8 16 32 64 128 256 512 1024)

procedure

(intersperse elem seq)  sequence?

  elem : any/c
  seq : sequence?

procedure

(add-between elem seq)  sequence?

  elem : any/c
  seq : sequence?
Similar to add-between, create a new sequence by adding elem between the elements of seq.

Examples:
> (intersperse 'and '(x y z))

'(x and y and z)

> (intersperse 'and '(x))

'(x)

> (intersperse "," '("a" "b" "c" "d"))

'("a" "," "b" "," "c" "," "d")

procedure

(wrap-each before after seq)  sequence?

  before : any/c
  after : any/c
  seq : sequence?
Create a new sequence by wrapping each element of seq with before and after.

Examples:
> (wrap-each '< '> '(x y z))

'(< x > < y > < z >)

> (wrap-each '< '> '(x))

'(< x >)

> (join-with " " (wrap-each "fresh" "and" '("apples" "bananas" "cherries")))

"fresh apples and fresh bananas and fresh cherries and"

> ((join (wrap-each ->string ->number (list add1 sqr))) "3")

"10"

procedure

(join-with elem seq)  any/c

  elem : any/c
  seq : sequence?
Similar to join, but intersperses elem between the elements of seq prior to joining them together. The result is of the same type as elem and the members of seq.

Examples:
> (join-with " " (list "hello" "there" "old" "friend"))

"hello there old friend"

> (display (join-with "\n" (list "Item 1" "Item 2" "Item 3")))

Item 1

Item 2

Item 3

> (join-with '(0 0) (stream '(1 2 3) '(4 5 6) '(7 8 9)))

'(1 2 3 0 0 4 5 6 0 0 7 8 9)

> (join-with 1 (list 1 2 3 4))

13

> ((join-with (λ (n)
                 (displayln n)
                 n)
              (list number->string sub1 sqr add1 sqr))
   3)

9

10

100

99

"99"

procedure

(weave before after seq)  sequence?

  before : any/c
  after : any/c
  seq : sequence?
Similar to join, but wraps each element of seq with to and from prior to joining them together. The result is of the same type as to, from, and the members of seq.

Examples:
> ((weave ->string
          ->number
          (list add1
                ((^ 2) add1)
                ((^ 3) add1)))
   "7")

"13"

> (weave "fresh " " and " '("apples" "bananas" "cherries"))

"fresh apples and fresh bananas and fresh cherries and "

> ((weave ->string ->number (list add1 sqr)) "3")

"10"

procedure

(range end)  stream?

  end : number?
(range start end [step])  stream?
  start : number?
  end : number?
  step : number? = 1
Identical to range, except that it includes additional runtime annotations to support isomorphic behavior.

procedure

(map proc seq ...+)  sequence?

  proc : procedure?
  seq : sequence?
Identical to map from data/collection, except that it includes additional runtime annotations to support isomorphic behavior.

Examples:
> (map sqr (list 1 2 3))

'(1 4 9)

> (map sqr #(1 2 3))

'#(1 4 9)

3.8 Composing🔗ℹ

Compose new sequences from given sequences. Not to be confused with defining sequences.

procedure

(zip seq ...)  (sequenceof list?)

  seq : sequence?

procedure

(zip-with op seq ...)  (sequenceof any/c)

  op : procedure?
  seq : sequence?

procedure

(unzip seq)  (sequenceof list?)

  seq : sequence?

procedure

(unzip-with op seq)  (sequenceof any/c)

  op : procedure?
  seq : sequence?
zip-with merges the input sequences using the provided operation op. Equivalent to Haskell’s zipWith.

zip is equivalent to zip-with list.

zip is its own inverse, so applying it twice is essentially equivalent to doing nothing. Still, unzip-with and unzip are provided with a slightly different signature – accepting a single sequence rather than an arbitrary number of sequences, making it convenient to apply to a result already derived by using zip. unzip is equivalent to (curry apply zip).

Examples:
> (zip (list 'a 'b 'c) (list 1 2 3 4 5))

(finite-sequence #<stream>)

> (zip-with + (list 1 2 3) (list 3 2 1))

(finite-sequence #<stream>)

> (->list (zip-with expt (repeat 5) (range 10)))

'(1 5 25 125 625 3125 15625 78125 390625 1953125)

> (->list (zip-with (lambda (x y)
                      (+ (* 2 x)
                         y))
                    (range 1 5)
                    (range 5 9)))

'(7 10 13 16)

> (unzip (zip (list 'a 'b 'c) (list 1 2 3)))

'((a b c) (1 2 3))

procedure

(interleave seq ...)  sequence?

  seq : sequence?
Lazily form a sequence by taking elements one at a time, in turn, from each of the input sequences, stopping at the first input sequence that runs out of elements.

Examples:
> (interleave (list 1 2 3) (list 4 5 6) (list 7 8 9))

'(1 4 7 2 5 8 3 6 9)

> (interleave (list 'a 'b 'c) (list 1 2))

'(a 1 b 2 c)

> (->list (take 10 (interleave (naturals 1) (cycle (list 'A 'B)))))

'(1 A 2 B 3 A 4 B 5 A)

> (->list (interleave (naturals 1) (list 'P 'Q 'R 'S 'T) (cycle (list 'a 'b))))

'(1 P a 2 Q b 3 R a 4 S b 5 T a 6)

procedure

(choose pred seq ...)  sequence?

  pred : procedure?
  seq : sequence?
Lazily choose a single item from each of the input sequences – the first one that fulfills the choice predicate pred. The result is a sequence containing as many values as the number of input sequences. If no item in a particular sequence fulfills the choice predicate, then the corresponding element in the resulting sequence is #f.

Examples:
> (choose number? (list 10 "left shoe" 30) (list "right shoe" 15 15) (list "sock" -55 7))

'(10 15 -55)

> (choose positive? (list -1 -2 1 2) (list -5 3 -2) (list 5 2 -1))

'(1 3 5)

> (choose (curry prefix? "ap") (list "banana" "apple" "apricot") (list "dog" "cat" "ape"))

'("apple" "ape")

3.9 Permuting🔗ℹ

Rearrange the elements of sequences.

procedure

(rotate-left n seq)  sequence?

  n : exact-nonnegative-integer?
  seq : sequence?

procedure

(rotate-right n seq)  sequence?

  n : exact-nonnegative-integer?
  seq : sequence?

procedure

(rotate seq)  sequence?

  seq : sequence?

procedure

(rotations seq)  (sequenceof sequence?)

  seq : sequence?
Derive a new sequence by shifting the elements of seq to the left or to the right, wrapping around the tail of the list. rotate-left eagerly processes n elements of seq and otherwise lazily evaluates the result, while rotate-right processes the entire list and therefore is not lazy.

rotate is equivalent to (curry rotate-left 1).

rotations yields all distinct rotations of seq.

Examples:
> (->list (rotate-left 1 (range 1 8)))

'(2 3 4 5 6 7 1)

> (->list (rotate-right 1 (range 1 8)))

'(7 1 2 3 4 5 6)

> (->list (rotate-left 3 (range 1 8)))

'(4 5 6 7 1 2 3)

> (->list (rotate-right 3 (range 1 8)))

'(5 6 7 1 2 3 4)

> (rotate-left 2 "avocado")

"ocadoav"

> (rotate-right 2 "avocado")

"doavoca"

> (rotate "avocado")

"vocadoa"

> ((power rotate 3) "avocado")

"cadoavo"

> (->list (rotations '(1 2 3)))

'((1 2 3) (2 3 1) (3 1 2))

> (->list (rotations "cherry"))

'("cherry" "herryc" "errych" "rryche" "rycher" "ycherr")

> (->list (truncate (iterate rotate "cherry") "cherry"))

'("cherry" "herryc" "errych" "rryche" "rycher" "ycherr")

procedure

(reverse seq)  sequence?

  seq : sequence?
Identical to reverse from data/collection, except that it includes additional runtime annotations to support isomorphic behavior.

Examples:
> (reverse "apple")

"elppa"

> (reverse #(1 2 3))

'#(3 2 1)