Elisp中多个列表的交集

5

如何使用 elisp 获取多个列表的交集?我是 elisp 新手,但我想肯定有一些内置函数或者通过 reduce 实现更好的解决方案。我拼凑了以下代码,但它似乎过于复杂。

;; get the intersection of these lists
;; result should be (3 4 5)
(setq test '((0 1 2 3 4 5) (2 3 4 5 6) (3 4 5 6 7)))

(require 'cl-lib)
(cl-remove-if-not
 (lambda (x) (cl-every
         (lambda (y) (> (length (memq x y) ) 0 ) )
         (cdr test) ) )
 (car test) )
;; ( 3 4 5)
2个回答

7

有一个cl-intersection仅接受两个操作数:

(cl-intersection '(0 1 2 3 4 5) '(2 3 4 5 6))

你可以使用它来定义自己的交集:
(defun my-intersection(l)
    (cond ((null l) nil)
          ((null (cdr l)) (car l))
          (t (cl-intersection (car l) (my-intersection (cdr l))))))

(my-intersection '((0 1 2 3 4 5) (2 3 4 5 6) (3 4 5 6 7)))

更新

感谢下面@Tobias的评论,你可以在新的函数中使用与cl-intersection相同的关键字参数,即(:test :test-not :key),并将它们传播到递归内部所有对它的调用。

以下是扩展版本:

(defun my-intersection(l &rest cl-keys)
    (cond ((null l) nil)
          ((null (cdr l)) (car l))
          (t (apply 'cl-intersection (car l) (apply 'my-intersection (cdr l) cl-keys) cl-keys))))

2
建议:增加一个参数&rest rest,并将cl-intersection替换为(apply 'cl-intersection (car l) (apply 'my-intersection (cdr l) rest) rest)。以此方式,您可以使用可选参数,如:test等。也许作为扩展版本。 - Tobias
如果我想用向量做同样的事情怎么办? - Cameron

4

安装第三方列表操作库 dash(请按照说明进行安装)。然后您需要:

(-reduce '-intersection '((1 2 3 4) (2 3 4 5) (3 4 5 6))) ; => (3 4)

如果您需要一个接受可变数量列表而不是单个列表的函数,请使用 &rest 关键字将其包装在一个函数中,如下所示:
(defun -intersection* (&rest list-of-lists)
  (-reduce '-intersection list-of-lists))
;; (-intersection* '(1 2 3 4) '(2 3 4 5) '(3 4 5 6)) ; => (3 4)

如果您是第一次使用 -reduce,那么它是一个“折叠”函数:它接收一个二元函数和一个元素列表,并逐个将它们缩减为最终结果的一个列表元素。 这篇答案 解释了折叠的概念。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接