我有这样的数据集:'(("red" 3 5)("blue" 6 8)...)
当键是字符串时是否可以使用assoc
?在这个简单的测试中,没有任何明显的尝试对我有效:
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
最佳答案
如果确定列表只包含字符串,则可以使用类型特定的函数string=
(区分大小写)或string-equal
(不区分大小写)。
但是,这些函数也接受符号,以及符号和字符串的混合。
因此(assoc "ABC" list :test #'string=)
不仅可以找到键"ABC"
,还可以找到任何名为"ABC"
的符号,例如符号:abc
或cl-use:abc
或mypackage:abc
。
用于比较任意两个对象的泛型equal
和equalp
函数不具有此行为和前面提到的两个一样,equal
和equalp
分别是区分大小写和不区分大小写的然而,他们也比较其他种类的物体。
与string=
和string-equal
不同,equal
和equalp
不认为字符串和符号是等价的;也就是说,(equalp "FOO" 'FOO) -> nil
。它们也不认为具有相同名称的符号是等效的:(equalp 'foo :foo) -> nil
当两个参数都是符号时,equal
和equalp
应用与eq
函数相同的测试。
因此,我认为,对关联列表的适当测试是两个函数equal
和equalp
之一,因为您混合了字符串和符号键。
这些功能还允许您的列表具有其他类型的键,如数字equalp
将按值比较数字,因此1和1.0是相同的键,而equal
更紧这两个函数都递归到列表中列表(1 2)
和(1 2)
是equal
的,即使它们不是同一个对象(单独使用),而(1 2)
和(1 2.0)
不是equal
,而是equalp
(除非您有一个非常奇怪的浮点系统)。此外,向量对象不是按equal
逐元素比较的,而是按equalp
比较的。
即使列表中只有字符串,还是最好使用这两个函数。
你不会得到很多,如果有的话,性能上的好处。string=
仍然需要验证参数的类型以确保它们是受支持的类型,并根据参数的字符串和符号的组合进行分派equal
根据多种类型的可能性进行调度,但这可以有效地完成。
在Lisp中,习惯性地使用过于特定类型的函数,或者不适当地严格相等,是一种糟糕的做法。string=
是故意使用的,但不是为了保存机器周期,而是在必须将符号作为字符串或符号与字符串的混合进行比较的情况下使用例如,如果您正在实现loop
宏,则可以使用string=
来检测loop
子句单词,根据ansi common lisp规范,这些单词根据符号名被视为等效的。用户可以编写(loop :for x below 42 ...)
或(loop mypackage:for x below 42 ...)
。但是(loop "FOR" ...)
无效!因此,您不能只依赖于string=
;您必须验证子句单词是一个符号。