List utilities
To use the bindings from this module:
(import :std/misc/list)
length=?, length<? ... length>=?
(length=? lst1 lst2) -> boolean
(length<? lst1 lst2) -> boolean
(length<=? lst1 lst2) -> boolean
(length>? lst1 lst2) -> boolean
(length>=? lst1 lst2) -> boolean
lst1, lst2 := lists to compare
Compares the list lengths of both lst1 and lst2, and returns a truth value
(#t or #f).
| function | less efficient variant |
|---|---|
(length=? x y) | (= (length x) (length y)) |
(length<? x y) | (< (length x) (length y)) |
(length<=? x y) | (<= (length x) (length y)) |
(length>? x y) | (> (length x) (length y)) |
(length>=? x y) | (>= (length x) (length y)) |
These functions are potentially more efficient because they only need to compare the lists up until the point where they start to differ from one another. They will short-circuit once they encounter a difference instead of calculating both lengths up-front.
Also, either of these two lists is allowed to be circular, but not both.
Examples:
> (import :std/srfi/1)
> (def small (iota 10)) ; => (0 1 ... 9)
> (def large (iota 100)) ; => (0 1 ... 99)
> (length=? small large)
#f ; comparison stops as soon as small runs out of elements
> (length<? large (circular-list 1 2 3))
#t ; circular list never runs out of elements
> (length>=? (circular-list 0 1) [])
#t
length=n?, length<n? ... length>=n?
(length=n? lst n) -> boolean | error
(length<n? lst n) -> boolean | error
(length<=n? lst n) -> boolean | error
(length>n? lst n) -> boolean | error
(length>=n? lst n) -> boolean | error
lst := list to compare
n := number
Checks how the length of lst compares to n and returns a truth value result
(#t or #f). Signals an error when n isn't a valid number.
| function | less efficient variant |
|---|---|
(length=n? x n) | (= (length x) n) |
(length<n? x n) | (< (length x) n) |
(length<=n? x n) | (<= (length x) n) |
(length>n? x n) | (> (length x) n) |
(length>=n? x n) | (>= (length x) n) |
These functions are potentially more efficient because they only need to check the list for up to n elements instead of calculating lst's length up-front.
Also, lst is allowed to be circular.
Examples:
> (length=n? [#\a #\b #\c] 3)
#t
> (import :std/srfi/1)
> (length>=n? (circular-list 0 1) 5)
#t
> (length<n? (circular-list 1 2 3) 10000)
#f ; circular list never runs out of elements
call-with-list-builder
(call-with-list-builder proc) -> list
proc := procedure that takes two proc identifiers as input
Takes a procedure or lambda proc which itself takes two procedures that can have any name but are called put! and peek here:
- put! will append its input element onto an internal list (and thus modifies it) on each invocation.
- peek retrieves the elements collected so far, or
[]if put! is never called.
Finally, call-with-list-builder returns the constructed list.
Examples:
> (import :std/iter)
> (call-with-list-builder
(lambda (put! peek)
(for (x (in-range 5 10))
(displayln (peek))
(put! (random-integer (1+ x))))))
() ; no prior put!
(5)
(5 6)
(5 6 2)
(5 6 2 8) ; fifth explicit peek call
(5 6 2 8 6) ; peek is called implicitly at the end
with-list-builder
(with-list-builder (put! [peek]) body ...) -> list
put! := function identifier that modifies internal list
peek := optional function identifier that retrieves internal list
Syntax sugar for the call-with-list-builder procedure, so put! and peek can
be used without wrapping them in a lambda first. with-list-builder returns the
internal list at the end.
Examples:
> (import :std/iter)
> (with-list-builder (put!)
(for (n (in-iota 100 1))
(let ((mod3 (zero? (modulo n 3)))
(mod5 (zero? (modulo n 5))))
(put! (cond ((and mod3 mod5) "fizzbuzz")
(mod3 "fizz")
(mod5 "buzz")
(else n))))))
(1 2 "fizz" 4 "buzz" "fizz" ... 97 98 "fizz" "buzz")
snoc
(snoc elem lst) -> list | error
elem := element to append to lst
lst := proper list
snoc is similar to cons, but appends elem at the end of lst instead of
putting it at the front.
Different from cons: snoc will signal an error when lst is not a proper
list. cons, in contrast, constructs a pair out of these two input values.
Examples:
> (cons 4 [1 2 3])
(4 1 2 3)
> (snoc 4 [1 2 3])
(1 2 3 4)
> (cons 1 2)
(1 . 2)
> (snoc 1 2)
error ; expects a list as second argument
> (snoc '(a b c) '())
((a b c))
append1
(append1 lst elem) -> list | error
lst := proper list
elem := element to append to lst
append1 is similar to append, but is constructing a proper list whereas the
latter returns an improper list when appending a non-list elem to lst.
append also joins two or more input lists while append1 simply adds the
second list as-is.
Signals an error when lst is not a list.
Examples:
> (append [1 2 3] 4)
(1 2 3 . 4)
> (append1 [1 2 3] 4)
(1 2 3 4)
> (append [1 2 3] [4 5 6])
(1 2 3 4 5 6)
> (append1 [1 2 3] [4 5 6])
(1 2 3 (4 5 6))
> (append1 "raise" "error")
error ; expects a list as first argument
for-each!
(for-each! lst proc) -> void
lst := proper or even improper list
proc := procedure called for side-effects
for-each! is similar to for-each, but the arguments lst and proc are
swapped which allows better nesting. Another slight difference: for-each! even
accepts improper lists.
Examples:
> (def exprs [[2 + 0] [2 - 0] [0 * 2] [2 / 0] [0 / 2]])
> (for-each (match <>
([x (eq? /) (? zero? y)]
(displayln "div by zero!"))
([x op y]
(displayln (op x y))))
exprs)
> (for-each! exprs
(match <>
([x (eq? /) (? zero? y)]
(displayln "div by zero!"))
([x op y]
(displayln (op x y)))))
;; both print:
2
2
0
div by zero!
0
> (for-each displayln '(1 2 . 3))
error ; list expected
> (for-each! '(1 2 . 3) displayln)
1
2 ; dotted list ending not included
push!
(push! elem lst) -> unspecified | error
elem := element to cons onto lst
lst := list
Macro that conses elem onto lst and set!s lst accordingly. lst needs
to be bound beforehand or it signals an error. It's unspecified what push!
returns otherwise.
Examples:
> (def lst [])
> (push! 10 lst)
> (push! 20 lst)
> (push! 30 lst)
> lst
(30 20 10)
> (def pair [#\b . #\a])
> (push! #\c pair)
> pair
(#\c #\b . #\a)
> (push! 1 [2 3])
error ; uses set! internally, requires valid binding
pop!
(pop! lst) -> #f | elem
elem := first element from lst
lst := list, from which the first element will be removed
Macro that checks whether the lst is a cons cell, if so returns the car of that cons cell,
and sets lst to the cdr of that cons cell, otherwise returns #f.
Examples:
> (def lst [])
> (pop! lst)
#f
> (push! 10 lst)
> (push! 20 lst)
> (pop! lst)
20
> (pop! lst)
10
> (pop! lst)
#f
flatten
(flatten lst) -> list
lst := proper nested list-of-lists
Removes all layers of nesting from lst, which is expected to be a proper list-of-lists (or tree structure). It will ignore any empty lists it encounters while traversing, not adding them to the returned flattened list.
Examples:
> (flatten [1 [2 3] [[4 5]]])
(1 2 3 4 5)
> (flatten [1 [] [2 [3 [] 4] 5]])
(1 2 3 4 5) ; ignores empty sublists
> (flatten '((a . 1) (b . 2) (c . 3)))
(a b c) ; expects proper non-dotted list-of-lists
flatten1
(flatten1 lst) -> list | error
lst := proper nested list-of-lists
flatten1 is a special variant of flatten which will not flatten the whole
nested list-of-lists (or tree structure), but instead removes only a single layer of
nesting from lst.
Note: lst is expected to be a list of proper lists, association lists will signal an error.
Examples:
> (flatten1 [1 [2 3] [[4 5]]])
(1 2 3 (4 5))
> (flatten1 [1 [] [2 [3 [] 4] 5]])
(1 2 (3 () 4) 5)
> (import :std/srfi/1)
> (map (cut iota <>) [1 2 3 4]
((0) (0 1) (0 1 2) (0 1 2 3))
> (flatten (map (cut iota <>) [1 2 3 4]))
(0 0 1 0 1 2 0 1 2 3)
> (flatten1 '((a . 1) (b . 2) (c . 3)))
error ; expects proper non-dotted list-of-lists
unique
unique!
(unique lst [test = equal?]) -> list
lst := proper list
test := test procedure which takes two arguments, defaults to equal?
Alias for delete-duplicates and delete-duplicates! (SRFI 1).
Examples:
> (unique [1 2 3 2])
(1 2 3)
> (let (lst [1 2])
(unique [lst lst] ev?))
((1 2))
duplicates
(duplicates lst [test = equal?] [key: #f]) -> list
lst := proper list
test := test procedure which takes two arguments, defaults to equal?
key := optional unary procedure to get the to compare item out of a list element
duplicates returns a cons cells (item . count) for every element
that occurs more than once in lst. If key: is not #f
the unary procedure is applied to every element before comparison.
Examples:
> (duplicates ['a 1 'a])
((a . 2))
> (duplicates [1 2 3])
()
> (duplicates '((a . 10) (b . 10)) key: cdr)
((10 . 2))
rassoc
(rassoc elem alist [pred = eqv?]) -> pair | #f
elem := element to search for in alist
alist := association list
pred := comparison predicate, optional
rassoc is similar to assoc, but instead of comparing elem with the first
element of each pair in alist the optional predicate pred (which defaults to
eqv?) will compare with the pair's second element.
Returns the first pair in alist whose cdr satisfies the predicate pred, or #f
otherwise.
Examples:
> (rassoc 2 '((a . 1) (b . 2) (c . 2) (d . 1)))
(b . 2)
> (rassoc "a" '((1 . "a") (2 . "b")))
#f ; eqv? is used by default
> (rassoc "a" '((1 . "a") (2 . "b")) string=?)
(1 . "a")
> (rassoc '(5 6) '((a 1 2) (b 3 4) (c 5 6)) equal?)
(c 5 6) ; equivalent to '(c . (5 6))
when-list-or-empty
(when-list-or-empty lst body ...) -> body ... | []
lst := value or list on which expansion depends
Macro which expands to body expressions only if lst is a non-empty list, otherwise an empty list is returned.
Examples:
> (let (nums [1 2 3])
(when-list-or-empty nums
(cdr nums)))
(2 3)
> (when-list-or-empty []
(cons "never" "expanded"))
()
slice
(slice lst start [limit = #f]) -> list
lst := proper list
start := start index
limit := number of items to take from lst
Returns a list from lst, starting from the left at start,
containing limit elements.
Examples:
> (slice [1 2 3 4] 2)
(3 4)
> (slice [1 2 3 4] 2 1)
(3)
slice-right
(slice-right lst start [limit = #f]) -> list
lst := proper list
start := start index from the right of lst
limit := number of items to take from lst
Returns a list from lst, starting from the right at start,
containing limit elements.
Examples:
> (slice-right [1 2 3 4] 2)
(1 2)
> (slice-right [1 2 3 4] 2 1)
(2)
slice!
(slice! lst start [limit = #f]) -> list
lst := proper list
start := start index
limit := number of items to take from lst
Returns a sublist by potentially updating the input list lst.
Starting from the left at start, containing limit elements.
Examples:
> (def lst [1 2 3 4 5])
> (slice! lst 2 2)
(3 4)
slice-right!
(slice-right! lst start [limit = #f]) -> list
lst := proper list
start := start index from the right of lst
limit := number of items to take from lst
Returns a sublist by potentially updating the input list lst.
Starting from the right at start, containing limit elements.
Examples:
> (def lst [1 2 3 4 5])
> (slice-right! lst 2 2)
(2 3)
butlast
(butlast lst) -> list
lst := proper list
butlast returns a copy of the proper list lst, except the last element.
When lst is empty, lst is returned as it is.
Examples:
> (butlast [1 2 3])
(1 2)
> (butlast [])
()
split
(split lst proc [limit = #f]) -> list
lst := proper list
stop := value or unary procedure
limit := optional, split the list only limit times
split the list lst into a list-of-lists using the value or
unary procedure stop. If limit is set, split the list only limit times.
Examples:
(split '(1 2 "hi" 3 4) string?)
> ((1 2) (3 4))
(split [1 2 0 3 4 0 5 6] 0 1)
> ((1 2) (3 4 0 5 6))
(split [] number?)
> ()
take-until
take-until!
(take-until pred lst) -> list
pred := predicate
lst := proper or circular list
take-until returns a list with all elements before pred returns #t.
take-until! is the linear-update variant of take-until.
Examples:
> (take-until number? ['a "hi" 1 'c])
(a "hi")
drop-until
(drop-until pred lst) -> list
pred := predicate
lst := proper or circular list
drop-until returns a list with all elements from the point on pred returns #t.
Examples:
> (drop-until number? ['a [] "hi" 1 'c])
(1 c)
group
(group lst [test = equal?]) -> list
lst := proper list
test := optional, binary predicate
group consecutive elements of the list lst into a list-of-lists.
Examples:
> (group [1 2 2 3 1 1])
((1) (2 2) (3) (1 1))
> (import :std/sort)
> (group (sort [1 2 2 3 1 1] <) eqv?)
((1 1 1) (2 2) (3))
every-consecutive?
(every-consecutive? pred lst)
returns a boolean that is true if any two consecutive terms in the list satisfy the predicate. In particular, if the predicate is a partial order predicate (respectively a strict partial order predicate), then the list is totally ordered (respectively strictly totally ordered) according to the predicate.
Examples:
> (every-consecutive? <= [1 2 2 3 10 100])
#t
> (every-consecutive? < [5 1 8 9])
#f
first-and-only
(first-and-only lst)
returns the first and only value of a singleton list, or raises an error if the argument wasn't a singleton list.
Examples:
> (first-and-only '(42))
42
> (first-and-only '())
*** ERROR IN std/misc/list#first-and-only, -515899390@648.11 -- Assertion failed (and (pair? x) (null? (cdr x)))