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.
(assoc "ABC" list :test #'string=) will find not only the key
"ABC" but also any symbol whose name is
"ABC", such as the symbol
equalp functions for comparing any two objects do not have this behavior. Like the aforementioned two,
equalp are, respectively, case sensitive and insensitive. However, they also compare other kinds of objects.
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,
equalp apply the same test as the
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
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
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 the
loopmacro, you might use
string=for detecting the
loopclause 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 on
string=; you'd have to validate that the clause word is a symbol.