I have a data set like this: '(("red" 3 5)("blue" 6 8)...)
Is it possible to use assoc
when the keys are strings? None of the obvious attempts have worked for me in this simple test:
CL-USER> (defparameter ggg (list '("foot" 2) '(bar 5)))
GGG
CL-USER> ggg
(("foot" 2) (BAR 5))
CL-USER> (assoc 'bar ggg)
(BAR 5)
CL-USER> (assoc "foot" ggg)
NIL
CL-USER> (assoc '"foot" ggg)
NIL
CL-USER> (assoc 'foot ggg)
NIL
If you are sure that your list contains only strings, you can use the type-specific functions string=
(case sensitive) or string-equal
(case insensitive).
However, these functions also accept symbols, and mixtures of symbols and strings.
Thus (assoc "ABC" list :test #'string=)
will find not only the key "ABC"
but also any symbol whose name is "ABC"
, such as the symbol :abc
or cl-use:abc
or mypackage:abc
.
The generic equal
and equalp
functions for comparing any two objects do not have this behavior. Like the aforementioned two, equal
and equalp
are, respectively, case sensitive and insensitive. However, they also compare other kinds of objects.
Unlike string=
and string-equal
, equal
and equalp
do not consider strings and symbols to be equivalent; that is, (equalp "FOO" 'FOO) -> nil
. They also do not consider symbols having the same name to be equivalent: (equalp 'foo :foo) -> nil
. When both arguments are symbols, equal
and equalp
apply the same test as the eq
function.
So I would argue that an appropriate test for your associative list, since you have a mixture of string and symbolic keys, is one of the two functions equal
and equalp
.
These functions will also allow your list to have other kinds of keys like numbers. equalp
will compare numbers by value, so that 1 and 1.0 are the same key, whereas equal
is tighter. Both these functions recurse into lists. The lists (1 2)
and (1 2)
are equal
even if they are not the same object (separately consed), whereas (1 2)
and (1 2.0)
are not equal
, but are equalp
(unless you have a very weird floating-point system). Also vector objects are not compared element-by-element by equal
, but they are by equalp
.
Even if you had strings only in the list, it's still better to use these two functions.
You are not going to get much, if any, performance benefit. string=
still has to validate the types of the arguments to make sure they are supported types, and dispatch according to what combination of string and symbol the arguments are. equal
dispatches according to numerous type possibilities, but this can be done efficiently.
Using overly type-specific functions, or inappropriately strict equality, as a matter of habit, is a poor practice in Lisp.
string=
is used deliberately, however, not for saving machine cycles, but in situations when symbols must be compared as strings, or mixtures of symbols and strings. For instance, if you were implementing theloop
macro, you might usestring=
for detecting theloop
clause words, which according to the ANSI Common Lisp spec are treated as equivalent based on symbol name. Users can write(loop :for x below 42 ...)
or(loop mypackage:for x below 42 ...)
. However(loop "FOR" ...)
is not valid! So you could not rely only onstring=
; you'd have to validate that the clause word is a symbol.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With