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
snoc, append1
(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.
Note that snoc
and append1
have the same function body, just with
their two arguments swapped.
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.
Note that the argument order is element first and list second,
as is traditional with the original Lisp PUSH
macro.
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 tree) -> list
tree := nested list of lists
Flatten a tree
into a list. The argument tree
can be a list of lists,
where pairs are branching nodes and the empty list is an empty tree, and
the elements that are neither pairs nor empty lists are values to be accumulated
left to right into a list, that is returned.
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) d . 4)))
(a 1 b 2 c 3 d 4) ; improper list terminators are tree elements like others
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
(when/list cond lst) -> list | []
cond := expression that evaluates to a generalized boolean
lst := expression evaluated and returned if cond was true
when/list
is like when
but returns []
instead of (void)
when the condition is false.
It is meant to be used when a list is expected, for instance, a list of options or keyword arguments,
that are only defined when some feature is enabled or other condition is true.
Examples:
> (when/list #t [mykw: 42])
(mykw: 42)
> (when/list #f [mykw: 42])
()
> (when/list #f (error "foo"))
()
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"))
()
listify
(listify x) -> list
x := expression that is returned if a list, or else [] is returned
listify
always returns a list: when the argument x
is a list, it is returned,
otherwise return the empty list []
.
Examples:
> (listify 42)
()
> (listify [])
()
> (listify ["proper" "list"])
("proper" "list")
> (listify ["improper" . "list"])
("improper" . "list")
keyword-when
(keyword-when cond [value]) -> list
keyword := arbitrary value, usually a keyword
cond := expression that if true, triggers the keyword-value list to be returned
value := expression evaluated if cond is true, returned as the value in the list
keyword-when
always returns a list: when the cond
is true, the list
[keyword value]
is returned, otherwise the empty list []
.
If value
is not specified, the non-false value returned by cond
is used
(but cond
is only evaluated once, in case it has side-effects).
keyword
doesn't actually have to be a keyword.
Examples:
> (keyword-when mykw: #t 42)
(mykw: 42)
> (keyword-when mykw: #f 42)
()
> (keyword-when mykw: "true")
(mykw: "true")
> (keyword-when mykw: #f)
()
> (keyword-when mykw: #f (error "foo"))
()
> (keyword-when "not a keyword" #t 42)
("not a keyword" 42)
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-consecutive
(group-consecutive lst [test = equal?]) -> list
lst := proper list
test := optional, binary predicate
group consecutive elements of the list lst
into a list-of-lists
wherein elements that satisfy the predicate with the previous element
are put in the same list, but those that don't are put in the next list.
Examples:
> (group-consecutive [1 2 2 3 1 1])
((1) (2 2) (3) (1 1))
> (import :std/sort)
> (group-consecutive (sort [1 2 2 3 1 1] <) eqv?)
((1 1 1) (2 2) (3))
> (group-consecutive [1 2 3 2 2 3 4 0 3 5] <)
((1 2 3) (2) (2 3 4) (0 3 5))
group-n-consecutive
(group-n-consecutive n lst) -> list-of-list
n := a positive integer
lst := list
group elements of the list lst
into clusters of n
elements, resulting
in a a list-of-lists that when concatenated return a list equal to lst
.
each element of the result list is a non-empty list, and the last element
and that element only may have fewer than n
elements, if n
does not
divide the length of the original list.
If lst
is an improper list, the last element of the result will be an
improper list with the same terminator.
Examples:
> (group-n-consecutive 2 [1 2 2 3 1 1])
((1 2) (2 3) (1 1))
> (group-n-consecutive 1 [1 2 3 4 5 6])
((1) (2) (3) (4) (5) (6))
> (group-n-consecutive 2 [1 2 3 4 5 6])
((1 2) (3 4) (5 6))
> (group-n-consecutive 2 [1 2 3 4 5 6 7])
((1 2) (3 4) (5 6) (7))
> (group-n-consecutive 3 [1 2 3 4 5 6 . 7])
((1 2 3) (4 5 6 . 7))
group-same
(group-same lst key: [key] table: [table]) -> list-of-list
lst := list
key := 1-argument function (default: identity)
table := a hash-table, hash-table-eq or hash-table-eqv (defaults: empty hash-table)
group elements of the list lst
that are have the same key
value
(default: that are the same), using the provided table to store the similar ones,
with "same" meaning equal according to the table's equality predicate
(which is equal?
by default, but the user may provide a different hash-table).
If the user provides a hash-table, it will be populated with the list of elements
for each key, reversed.
The elements in the resulting list appear in the order that
their first element appear in the original list,
and are each lists containing elements with the same key
in the same order as their appear in the original list.
Examples:
> (group-same [1 2 2 3 1])
((1 1) (2 2) (3))
> (group-same '("abc" "b" "c" "ef" "gh" "ijk") key: string-length)
(("abc" "ijk") ("b" "c") ("ef" "gh"))
map/car
(map/car f pair)
f := function acting on the car of the pair
pair := pair
returns a new pair where the first element (car) has been transformed by the function f
.
Examples:
> (map/car (cut * 10 <>) (cons 3 4))
(30 . 4)
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
separate-keyword-arguments
(separate-keyword-arguments args [positionals-only]) -> (values positional-args keyword-args)
Given a list of arguments passed to a function, return two values, the first one the list of positional arguments, in order, in the first list, the second one the plist of keyword arguments with each provided keyword followed by the corresponding argument value.
If positionals-only
is false (the default), then the #!key
and #!rest
markers
are kept into the list in the first value, so the list can be used in a call to apply
to another function to keep passing those values, without the keyword arguments
(or with the keyword arguments prepended in front).
If positionals-only
is true, then the #!key
and #!rest
markers
are filtered off so the first value is ready to be matched positionally
against a list of variables of the same length (and/or with a rest argument).
Examples:
> (separate-keyword-arguments '(x a: 1 y b: 2 c: 3 z))
(x y z)
(a: 1 b: 2 c: 3)
> (separate-keyword-arguments '(x a: 1 y #!key b: 2 c: 3 z #!rest t d: 4) #t)
(x y b: 2 z t d: 4)
(a: 1 c: 3)
> (separate-keyword-arguments '(x a: 1 y #!key b: 2 c: 3 z #!rest t d: 4) #f)
(x y #!key b: 2 z #!rest t d: 4)
(a: 1 c: 3)
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)))
with-list-builder, call-with-list-builder
This macro and this function are re-exported from :std/misc/list-builder