A-List utilities
Utilities to manipulate alists, i.e. association lists, i.e. a list of key-value pairs.
To use the bindings from this module:
(import :std/misc/alist)
alist?
(alist? alist) -> boolean
alist := association list to check
Checks whether alist is a proper association list and returns a truth value
(#t
or #f
). alist needs to be finite, circular lists are not supported.
A proper association list is a list of pairs and may be of the following forms:
((key1 . value1) ...)
((key1 value1) ...)
Examples:
> (alist? '((a . 1) (b . 2) (c . 3)))
#t
> (alist? [["one" #\1] ["two" #\2] ["three" #\3]])
#t
> (alist? '((a . 1) ("two" #\2) (1 2 3 4)))
#t ; (1 2 3 4) is equivalent to (1 . (2 3 4))
> (alist? '(a 1 b 2 c 3))
#f ; input is a plist, see plist? function
> (alist? '())
#t ; edge-case, just like (list? '()) => #t
plist->alist
(plist->alist plist) -> alist | error
plist := property list to transform
Transforms a property list (k1 v1 k2 v2 ...)
into an association list ((k1 . v1) (k2 . v2)...)
. plist needs to be finite, circular lists are not supported.
Furthermore, an error is signaled when plist is a improper property list.
Examples:
> (plist->alist [10 "cat" 11 "dog" 12 "bird"])
((10 . "cat") (11 . "dog") (12 . "bird"))
> (plist->alist ["semicolon" #\; "comma" #\, "dot"])
error ; key "dot" has no associated property value
> (plist->alist [])
()
assq-set!, assv-set!, assoc-set!
(asetq! alist key value)
(assq-set! key alist value)
(asetv! alist key value)
(assv-set! key alist value)
(aset! alist key value)
(assoc-set! key alist value)
These functions kind-of complement the assq
, assv
, assoc
functions from the prelude,
and enable the destructive update of an alist (association list), i.e. a association list
that follows the [[key1 . value1] [key2 . value2] ... [keyN . valueN]]
,
by either modifying in-place an entry with the given key, or adding a new entry.
The functions all return #!void
.
Just like the alist getter functions, these functions are distinguished by which
equality predicate is used to compare keywords: eq?
, eqv?
or equal?
respectively.
eq?
is best for keys being symbols and keywords, eqv?
for numbers, and equal?
for strings
or lists, etc.
Each function comes in two variant:
the first one accepts (asetq! alist key value)
as the order of arguments,
whereas the second one follows the convention so that you can use (set! (assq key alist) value)
or (set! (assv key alist) value)
.
Note that in the last case, assq
, assv
and assoc
return a pair,
whereas the setter functions take just the value as argument.
That's why we say these setter functions only "kind-of" complement the respective getter functions.
Last but not least, destructive operations are not allowed on an empty alist.
If you use aset!
or its friends, you have to ensure your alists are never empty.
For instance you may keep a dummy key at the end of your alist that never gets removed.
If this constraint is not acceptable, you may instead storing your alist
in a variable (or struct field), use the pure aset
operation, and update
the variable (or struct field) with the result of it.
Examples:
(let (p [['a . 1]['b . 2]]) (asetq! p 'a 3) p)
> [['a . 3]['b . 2]]
(let (p [['a . 1]['b . 2]]) (asetq! p 'c 3) p)
> [['c . 3]['a . 1]['b . 4]]
aremq!, aremv!, arem!
(aremq! key plist)
(aremv! key plist)
(arem! key plist)
These functions destructively modify an alist (association-list) to remove the entry for a given key.
Just like the alist getter and setter functions, these functions are distinguished by which
equality predicate is used to compare keywords: eq?
, eqv?
or equal?
respectively.
See aset!
above about alists. The functions all return #!void
.
It is not allowed to destructively remove the last entry in an alist.
If you use arem!
or its friends, you have to ensure your alists are never empty.
For instance you may keep a dummy key at the end of your alist that never gets removed.
If this constraint is not acceptable, you may instead storing your alist
in a variable (or struct field), use the pure arem
operation, and update
the variable (or struct field) with the result of it.
Examples:
(let (p [['a . 1]['b . 2]]) (aremq! 'a p) p)
> [['b . 2]]
(let (p [['a . 1]['b . 2]]) (arem! 'c p) p)
> [['a . 1]['b . 2]]
acons
(acons k v alist) -> alst
k := key
v := value
alist := association list
alst := new association list with additional binding
Adds a new key-value pair to an existing alist in a pure way, a bit like aset
.
Unlike aset
and its friends, however, acons
does not try to replace older bindings
for the same key, and will instead shadow them.
This can cause performance issues if a same key is used a lot of times with acons
,
with the list growing ever longer; this can cause even more interesting correctness issues
if arem
is used subsequently used, which will remove only the most recent binding,
revealing the previous one again, which may or may not be the desired behavior.
Thus, we recommend only using acons
when you otherwise know k
is not yet bound in the alist.
acons
is analogous to the same-named function from Common Lisp, and
(acons k v l)
is synonymous with (cons (cons k v) l)
.
Examples:
> (acons 1 "a" '((2 . "b") (3 . "c")))
((1 . "a") (2 . "b") (3 . "c"))
> (acons 1 "a" '((1 . "b")))
((1 . "a") (1 . "b"))