我有一个简单的Common Lisp问题:从字符串列表中删除重复项的惯用方法是什么?
remove-duplicates
正如我对数字所期望的那样,但不是字符串:
* (remove-duplicates '(1 2 2 3))
(1 2 3)
* (remove-duplicates '("one" "two" "two" "three"))
("one" "two" "two" "three")
我猜测有一些感觉,字符串不相等,很可能因为虽然“foo”和“foo”显然是相同的,但它们实际上是指向内存中不同结构的指针。我想我的期望可能只是一个C宿醉。
答案 0 :(得分:17)
你必须告诉remove-duplicatelicates它应该如何比较这些值。默认情况下,它使用eql
,这对于字符串来说是不够的。传递:test
函数,如下所示:
(remove-duplicates your-sequence :test #'equal).
(编辑以解决评论中的问题):作为equal
的替代方案,您可以在此示例中使用string=
。这个谓词(在某种程度上)不如equal
那么通用,它可能(可能,可能,最终......)因此更快。如果传递错误的值,string=
可以告诉您真正的好处:
(equal 1 "foo")
愉快地产生nil
,而
(string= 1 "foo")
给出type-error
条件。但请注意,
(string= "FOO" :FOO)
是完全明确定义的(string=
,其朋友是根据“字符串指示符”而不是字符串来定义的,所以类型安全只会到目前为止。
另一方面,标准eql
谓词几乎不是比较字符串的正确方法。如果您熟悉Java语言,请将eql
视为使用==
而equal
(或string=
等)调用equals(Object)
方法。虽然eql
进行了某种类型的内省(与eq
相反,但没有),对于大多数(非数字)lisp类型,eql
归结为类似指针比较的内容,如果你想根据它们实际包含的内容来区分值,而不仅仅是它们在内存中的位置,那么这是不够的。
对于更多Pythonic倾向,eq
(和eql
非数字类型)更像is
运算符,而equal
更像{{1}调用==
。