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)))