【问题标题】:Weird Common Lisp intersection behaviour奇怪的 Common Lisp 交叉行为
【发布时间】:2014-07-11 18:33:13
【问题描述】:

我正在尝试获取两个列表的共同元素。 我已经尝试了可用的交集函数和我自己实现的一个,当尝试在(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))))

我怎样才能在该类型的列表中获得好的结果?例如,我想要来自(intersection '(2 2 2 2) '(2 2 2 3))(2 2 2)

【问题讨论】:

  • 我正在使用 LispWorks 并且 INTERSECTION 函数在 COMMON-LISP 包中,所以我假设它是 CLISP? screenshot
  • 对不起,我有一种想法。在这种情况下,返回 (2 2 2 2) 的方法没有任何问题。 (出于某种原因,我在想unioncl:intersection 应该返回一个列表,其中包含第一个和第二个列表中的所有元素。如果第一个列表只包含第二个列表中的元素,那么第一个列表正好包含那些同时出现的元素,所以返回第一个列表不是错误。
  • @JoshuaTaylor:在你的第一条评论中,你说它返回了(2 2 3 2),我认为这是一个错字,你的意思是(2 2 2 2)
  • @alecutheman 好的,我知道你已经接受了答案,但是这里发生了一些有趣的事情,值得看看规范要求和允许的内容,所以我已经添加了有关此处实际情况的答案。有趣的是,LispWorks 中的(intersection '(2 2 2 3) '(2 2 2 2)) 确实返回了(2 2 2)

标签: set lisp common-lisp multiset


【解决方案1】:

您需要从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))))

【讨论】:

  • 我理解我的错误,按照你的代码它应该可以按预期工作,但它没有,它给出了相同的结果:p
  • 如果初始列表的长度为 3 或更大,它应该可以工作,但由于某种原因会中断:example
  • @alecutheman 我相信我对此进行了测试,但我必须复制旧版本。原始代码有一个错字,名称与递归的名称不同。现在是正确的:)
  • 我也错过了错字。非常感谢!
【解决方案2】:

已经有一个公认的答案,但我想指出实现提供的答案,在哪里

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

正确的。重要的是要认识到,intersection、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。一些与集合相关的定义名称。

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

交集操作描述如下。为了所有可能 由 list-1 中的一个元素和一个元素组成的有序对 从 list-2 中,:test 或 :test-not 用于确定它们是否 满足测试。 :test 或 :test-not 的第一个参数 function 是 list-1 的一个元素;第二个参数是 清单 2。如果 :test 或 :test-not 未提供,则使用 eql。它是一个 如果 :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 对的每一行和每一列。我想,这取决于“这对的两个元素中的一个将被放入结果中”的解释。不过,一般来说,如果你有,例如,lists-as-sets (a1 a2)(b1 b2 b3),那么你就有了这些对:

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

认为该规范应该被理解为每个aibi 将最多包含一次,并且您永远不会包含给定的aibi基于特定对(ai bi)。因此,如果您要从第一行中选择 (a1 b2) 并在结果中包含 b2,那么其余可能对结果有贡献的对是

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

如果你从(a1 b2) 中取出a1,那么剩下的对就是

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

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

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

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

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

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

CL-USER 2 > (let ((l1 '(2 2 2 2))
                  (l2 '(2 2 2 3)))
              (eq l1 (intersection l1 l2)))
T

【讨论】:

  • 我猜这很有趣,但我看不出这样的功能有什么用途。我希望交集运算具有交换性,至少在某种程度上,也就是说,即使顺序不同,至少也会得到具有相同元素的结果。
  • 确实有交换性。交叉口&al。用于列表集合。相同的集合用 (2)、(2 2)、(2 2 2) 和 (2 2 2 2) 表示。
【解决方案3】:

这是我的,效果很好。我使用 remove 删除重复的符号。

(defun my-intersection (x y)
   (cond ((or (null x) (null y)) nil)
     ((find (first x) y) (cons (first x)
                   (my-intersection (remove (first x) x) y)))
     (t (my-intersection (rest x) y))))

【讨论】:

    猜你喜欢
    • 2015-03-01
    • 1970-01-01
    • 2017-02-04
    • 2016-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多