我有一个这样的数据集:
'(("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=
子句单词,根据 ANSI Common Lisp 规范,这些单词根据符号名称被视为等效。用户可以写loop
或(loop :for x below 42 ...)
。然而(loop mypackage:for x below 42 ...)
无效!所以你不能仅依赖于(loop "FOR" ...)
;你必须验证从句单词是一个符号。string=
(assoc "foot" ggg :test #'string-equal)
或
(assoc "foot" ggg :test #'string=)
取决于您是否希望比较区分大小写。
? (assoc "foot" ggg :test #'equalp)
("foot" 2)
正如其他人已经指出的那样,您可以使用不同类型的密钥,如 HyperSpec:
中所述(setq alist '(("one" . 1)("two" . 2))) => (("one" . 1) ("two" . 2))
(assoc "one" alist) => NIL
(assoc "one" alist :test #'equalp) => ("one" . 1)
除了其他答案都很好之外,原始发帖者还有一个格式错误的关联列表。
'((“红”3 5)(“蓝”6 8)...
应该是
'((“红色”。(3 5))(“蓝色”。(6 8)...)