Seq: A Sequence Library
(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
Each of the modules below overrides or re-provides all of the interfaces in data/collection. Thus, it should generally suffice to require just one of the modules below, without also requiring data/collection.
(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
take and drop refer to elements, hence they return a sequence containing the relevant elements in the original sequence.
cut and infix refer to subsequences, hence they either accept or return subsequences of the original sequence conforming to the query. E.g. find-infix searches for a subsequence in the input, rather than an individual element. cut-when returns a sequence of subsequences cut from the original. Most string-related operations are of this kind.
find and remove without additional qualification operate on individual elements.
2.2 Suffixes
Undecorated verbs usually check for equality. E.g. trim removes the specified elements at the head and tail of a sequence (if present).
-where indicates a specific place with respect to the contents of the sequence, for instance cut-where splits the input sequence at a particular (the first) point where a given predicate evaluates to true.
-at indicates a specific place in the sequence by position, for instance cut-at splits the input sequence at the indicated index.
-when indicates a sequence-spanning condition – take-when takes all elements in the input sequence for which a predicate holds (more commonly known as filter).
-while indicates a running condition – e.g. take-while takes as long as a predicate holds, and then stops at the point where it fails.
-until indicates a running condition, the negation of "-while" – e.g. take-until takes as long as a predicate does not hold, and then stops at the point where it returns true.
-by indicates operations that deal in terms of lengths rather than the actual contents of the sequence. For instance, trim-by removes a certain number of elements at the head and tail of the sequence.
-with indicates a function to be used as part of the operation. For instance, zip-with "zips" sequences together by using a provided function to combine partnered elements.
-if indicates a sequence-spanning condition. trim-if removes elements at the head and tail of a sequence if some condition is met.
-unless is a sequence-spanning condition, the negation of "-if". E.g. trim-unless removes elements at the head and tail of a sequence unless some condition is met. Note that in general, "-unless" is avoided in favor of simply using the opposite verbs. For instance, in lieu of take-unless, there’s drop-when.
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
n : exact-nonnegative-integer? seq : sequence?
> (->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
n : exact-nonnegative-integer? seq : sequence?
procedure
n : exact-nonnegative-integer? seq : sequence?
procedure
n : exact-nonnegative-integer? seq : sequence?
procedure
n : exact-nonnegative-integer? seq : sequence?
procedure
n : exact-nonnegative-integer? seq : sequence?
> (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
start : exact-nonnegative-integer? length : exact-nonnegative-integer? seq : sequence?
procedure
start : exact-nonnegative-integer? end : exact-nonnegative-integer? seq : sequence?
> (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?
> (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?
> (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
pos : natural-number/c seq : sequence?
> (nth 3 (list 1 2 3 4 5)) 4
> (nth 4 "The quick brown fox") #\q
> (nth -1 (list "apple" "cherry" "banana")) "banana"
procedure
pos : natural-number/c new-elt : any/c seq : sequence?
> (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
pos : natural-number/c seq : sequence?
> (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")
> (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
pred : procedure? seq : sequence?
> (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 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
pred : procedure? seq : sequence?
procedure
pred : procedure? seq : sequence?
procedure
pred : procedure? seq : sequence?
> (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?
> (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?
> (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")
> (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-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 -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
left : exact-nonnegative-integer? right : exact-nonnegative-integer? seq : sequence?
> (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
pred : procedure? seq : sequence?
> (->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
elem : any/c seq : sequence? key : procedure? = #f
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, =.
> (->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?
> (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))
> (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))
> (->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))
> (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?
> (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?
> (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.
> (->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)
> (->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
len : exact-positive-integer? seq : sequence?
> (->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.
> (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
> (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
nfx : sequence? seq : sequence?
procedure
(starts-with? nfx seq ...) → boolean?
nfx : sequence? seq : sequence?
procedure
nfx : sequence? seq : sequence?
procedure
(ends-with? nfx seq ...) → boolean?
nfx : sequence? seq : sequence?
procedure
nfx : sequence? seq : sequence?
procedure
nfx : sequence? seq : sequence?
> (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
elem : any/c n : natural-number/c = 0
> (->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)
> (->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
f : procedure? elem : any/c
> (->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?
> (intersperse 'and '(x y z)) '(x and y and z)
> (intersperse 'and '(x)) '(x)
> (intersperse "," '("a" "b" "c" "d")) '("a" "," "b" "," "c" "," "d")
> (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"
> (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"
> ((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
end : number? (range start end [step]) → stream? start : number? end : number? step : number? = 1
procedure
proc : procedure? seq : sequence?
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 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).
> (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?
> (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
pred : procedure? seq : sequence?
> (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
seq : sequence?
procedure
(rotations seq) → (sequenceof sequence?)
seq : sequence?
rotate is equivalent to (curry rotate-left 1).
rotations yields all distinct rotations of seq.
> (->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")