我有一个这样的数据集:'(("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
答案 0 :(得分:9)
如果您确定列表中只包含字符串,则可以使用特定于类型的函数string=
(区分大小写)或string-equal
(不区分大小写)。
但是,这些函数也接受符号,符号和字符串的混合。
因此,(assoc "ABC" list :test #'string=)
不仅会找到键"ABC"
,还会找到名称为"ABC"
的任何符号,例如符号:abc
或cl-use:abc
或{ {1}}。
用于比较任何两个对象的通用mypackage:abc
和equal
函数没有此行为。与前面提到的两个一样,equalp
和equal
分别区分大小写且不敏感。但是,它们也比较了其他类型的对象。
与equalp
和string=
不同,string-equal
和equal
不认为字符串和符号是等价的;也就是equalp
。他们也不认为具有相同名称的符号是等效的:(equalp "FOO" 'FOO) -> nil
。当两个参数都是符号时,(equalp 'foo :foo) -> nil
和equal
会应用与equalp
函数相同的测试。
所以我认为对你的关联列表进行适当的测试,因为你有一个字符串和符号键的混合,是eq
和equal
这两个函数之一。
这些功能还允许您的列表包含其他类型的键,如数字。 equalp
将按值比较数字,因此1和1.0是相同的键,而equalp
更紧密。这两个函数都会递归到列表中。列表equal
和(1 2)
是(1 2)
即使它们不是同一个对象(单独使用),而equal
和(1 2)
不是(1 2.0)
},但是equal
(除非你有一个非常奇怪的浮点系统)。另外,矢量对象不是按equalp
逐个元素进行比较,而是equal
。
即使您只在列表中包含字符串,使用这两个函数仍然更好。
您不会获得太多(如果有的话)性能优势。 equalp
仍然需要验证参数的类型以确保它们是受支持的类型,并根据参数的字符串和符号的组合进行调度。 string=
根据众多类型的可能性发送,但这可以有效地完成。
在Lisp中使用过度类型特定的函数或不恰当的严格平等,这在Lisp中是一种不好的做法。
然而,
equal
是故意使用的,不是为了节省机器周期,而是在必须将符号作为字符串或符号和字符串的混合进行比较的情况下。例如,如果您正在实现string=
宏,则可以使用loop
来检测string=
子句,根据ANSI Common Lisp规范将其视为基于符号名称的等效。用户可以撰写loop
或(loop :for x below 42 ...)
。但是(loop mypackage:for x below 42 ...)
无效!因此,您无法依赖(loop "FOR" ...)
上的 ;你必须验证子句是一个符号。
答案 1 :(得分:6)
(assoc "foot" ggg :test #'string-equal)
或
(assoc "foot" ggg :test #'string=)
取决于您是否希望比较区分大小写。
答案 2 :(得分:2)
? (assoc "foot" ggg :test #'equalp)
("foot" 2)
答案 3 :(得分:0)
正如其他人已经指出的那样,您可以使用HyperSpec中描述的不同类型的密钥:
(setq alist '(("one" . 1)("two" . 2))) => (("one" . 1) ("two" . 2))
(assoc "one" alist) => NIL
(assoc "one" alist :test #'equalp) => ("one" . 1)