奇怪的Common Lisp交集行为

时间:2014-05-22 15:48:45

标签: set lisp common-lisp multiset

我试图获得两个列表的共同元素。 我已经尝试了可用的交集功能和我自己实现的功能,在尝试在(a a ... a)(a b c d ... z)等列表上测试它们时,两者都给出了相同的奇怪结果。

每当第一个列表只包含多个相同的元素,第二个列表以该元素开头时,结果就是第一个列表。

例如:(intersection '(2 2 2 2) '(2 2 2 3))返回(2 2 2 2)

我实施的交叉路口:

(defun presentp (a l)
  (cond ((null l) nil)
        ((and (atom (car l)) (equal a (car l))) t)
        ((not (atom (car l))) (presentp a (car l)))
        (t (presentp a (cdr l)))))

(defun intersectionp (a b)
  (cond ((not (and a b)) nil)
        ((presentp (car a) b) (append (list (car a)) (intersection (cdr a) b)))
        (t (intersection (cdr a) b))))

如何在该类型的列表上获得良好的结果?例如,我希望来自(2 2 2)的{​​{1}}。

3 个答案:

答案 0 :(得分:4)

您需要从b列表中删除匹配项。当您在(2 2 2 3)中找到2时,您应继续(2 2 3)作为b

此外.. (append (list x) result-list)(cons x result-list)相同,只是CPU周期相同或更少。

(defun intersection (a b)
  (cond ((not (and a b)) nil)
        ((presentp (car a) b)
         (cons (car a) 
               (intersection (cdr a) 
                             (remove (car a) b :count 1))))
        (t (intersection (cdr a) b))))

答案 1 :(得分:3)

已经有一个已接受的答案,但我想指出实现提供的答案,其中

(cl:intersection '(2 2 2 2) '(2 2 2 3))
;=> (2 2 2 2)

正确无误。重要的是要认识到交集,nintersection等旨在与被视为集合的列表一起使用。从概念上讲,一个集合没有重复的元素(因为你需要一个 multiset ),所以列表(2),(2 2),(2 2 2)等都代表了同一套,{2}。

  

14.1.2.2 Lists as Sets

     

列表有时通过考虑其元素被视为集合   无序的,并假设没有重复的元素。

adjoin         nset-difference    set-difference    union  
intersection   nset-exclusive-or  set-exclusive-or         
nintersection  nunion             subsetp
     

图14-5。一些与集合相关的已定义名称。

现在,关于“假设没有元素重复”的那一点实际上意味着你可能不应该使用带有像(2 2 2 2)这样的列表的set函数,因为它有明显的元素重复。即便如此,如果您认为像(2 2 2)和(2 2 2 2)这样的列表代表相同的,您可以看到intersection实际上正在为您提供正确的设置。我认为规范实际上要求结果将有三个或四个元素。来自intersection上的HyperSpec条目:

  

交叉操作描述如下。尽可能   有序对由list-1中的一个元素和一个元素组成   从list-2,:test或:test-not用于确定它们是否存在   满足测试。第一个参数:test或:test-not   function是list-1的一个元素;第二个参数是一个元素   表2。如果:不提供test或:test-not,则使用eql。它是一个   错误if:test和:test-not在同一个函数调用中提供。 ...

     

对于满足测试的每一对,恰好是两个元素中的一个   该对将被放入结果中。两个列表中都没有元素   出现在不满足元素测试的结果中   从另一个清单。如果其中一个列表包含重复元素,   结果可能有重复。

因此,对于(2 2 2 2)(2 2 2 3),需要考虑16对:

(2 2) (2 2) (2 2) (2 3) ; first element is first  2 from list-1, second elements are from list-2 
(2 2) (2 2) (2 2) (2 3) ; first element is second 2 from list-1, second elements are from list-2 
(2 2) (2 2) (2 2) (2 3) ; first element is third  2 from list-1, second elements are from list-2 
(2 2) (2 2) (2 2) (2 3) ; first element is fourth 2 from list-1, second elements are from list-2 

因为“对于满足测试的每一对,两个元素中只有一个将被放入结果中,”在我看来,你最终将会在结果中3到4 2之间,因为你有12对满足测试,你需要覆盖这12对中的每一行和每列。我想,这取决于“对这两个元素中的一个元素中的一个将被置于结果中”的解释。但一般情况下,如果你有,例如,list-as-sets (a1 a2)(b1 b2 b3)那么你就有了这些对:

(a1 b1) (a1 b2) (a1 b3)
(a2 b1) (a2 b2) (a2 b3)

认为该规范应该被理解为每个aibi最多只包含一次,并且您永远不会包含给定的{{1 }}和ai基于特定的对bi。因此,如果从第一行开始选择(ai bi)并在结果中包含(a1 b2),则剩余的可以为结果提供元素的对是

b2

如果您从(a1 b1) (a1 b3) (a2 b1) (a2 b3) 获取了a1,那么剩余的对将是

(a1 b2)

也就是说,当您从其中一对中包含元素时,您要么从确定可能结果的对表中删除行或列。在第一种情况下,您仍然可以在结果中添加两个元素,但在第二种情况下,可能会有三个元素。

事实上,在LispWorks中,如果颠倒参数的顺序,你将得到3元素版本:

(a2 b1) (a2 b2) (a2 b3)
  

无法保证结果中元素的顺序   以任何特定方式反映参数的顺序。该   结果列表可以与list-1或list-2共享单元格,或者与list-1或list-2共享单元格   如果合适的话。

你没有提到你是否只是获得一个等效的列表,或者你是否真的得到了list-1。在Lispworks中,似乎你实际上得到了相同的列表,尽管这不是必需的:

CL-USER 5 > (intersection '(2 2 2 3) '(2 2 2 2))
(2 2 2)

答案 2 :(得分:0)

这是我的运作良好。我使用删除来删除重复的符号。

sender.tag