Lisp /列表的交集

时间:2018-11-08 13:59:15

标签: lisp common-lisp set-intersection

你好,我想在 common-lisp 中创建一个函数,该函数接受两个列表,并输出它们的相交,假设每个列表中没有重复就不使用相交函数。看来它不起作用。有人可以帮忙吗?

(defun isect (lst_1 lst_2)
    (setq newlist nil)
    (dolist (x lst_1 newlist)
        (dolist (y lst_2) 
            (if (equal x y) (setf newlist (append newlist x)))
        )
    )
)

4 个答案:

答案 0 :(得分:1)

我假设两个参数都在同一列表中的isect应该返回相等的列表,而不是平坦的列表。在这种情况下,(append newlist x)不会在列表末尾添加元素。这是我的建议:

(defun intersect (lst-a lst-b &aux result)
  (dolist (a lst-a (nreverse result))
    (dolist (b lst-b)
      (when (equal a b)
        (push a result)))))

这是O(n ^ 2),而您可以使用哈希表在O(n)中进行操作。

答案 1 :(得分:0)

如果您可以确保列表已排序(升序),则可以执行类似的操作

(defun isect (l1 l2 acc)
  (let ((f1 (car l1))
        (f2 (car l2))
        (r1 (cdr l1))
        (r2 (cdr l2)))
    (cond ((or (null l1) (null l2)) acc)
          ((= f1 f2) (isect r1 r2 (cons f1 acc)))
          ((< f1 f2) (isect r1 l2 acc))
          ((> f1 f2) (isect l1 r2 acc)))))

但是请注意,结果是相反的顺序。此外,该示例还假设 元素是数字。如果想一概而论,可以将顺序作为可选参数传递,以使其适用于任意元素。

注意:使用loop的解决方案可能会更快,但是当car不同时,我想不出如何部分“推进”列表。

答案 2 :(得分:0)

一种内置方法(不适用于家庭作业;))是使用intersectionhttps://lispcookbook.github.io/cl-cookbook/data-structures.html#intersection-of-lists

列表a和列表b中都包含哪些元素?

(defparameter list-a '(0 1 2 3))
(defparameter list-b '(0 2 4))
(intersection list-a list-b)
;; => (2 0)

答案 3 :(得分:0)

;; the key function for simple lists
(defun id (x) x)

;; the intersect function for two lists
;; with sorting included:
;; you need an equality-test:
;;   default is #'eql (for simple numbers or symbols this is sufficient)
;;   - for numbers only    #'=
;;   - for characters only #'char=
;;   - for strings only    #'string=
;;   - for lists           #'equal
;;   - for nearly everything #'equalp (case insensitive for char/strings!)
;; then you need also a sorting tester:
;; - increasing number:  #'<
;; - decreasing number:  #'>
;; - increasing char:    #'char<
;; - decreasing char:    #'char>
;; - increasing strings: #'string<
;; - decreasing strings: #'string>
;; - other cases I haven't think of - does somebody have an idea?
;;   (one could sort by length of element etc.)
;;   so sort-test should be a diadic function (function taking 2 arguments to compare)
;; then you also need an accessor function
;;   so, how withing each element the to-be-sorted element should be accessed
;;   for this, I prepared the `id` - identity - function because this is the
;;   sort-key when simple comparison of the elements of the two lists
;;   should be compared - and this function is also used for testing
;;   for equality in the inner `.isect` function.
(defun isect (lst-1 lst-2 &key (equality-test #'eql) (sort-test #'<) (sort-key #'id))
  (let ((lst-1-sorted (stable-sort lst-1 sort-test :key sort-key))
        (lst-2-sorted (stable-sort lst-2 sort-test :key sort-key)))
    (labels ((.isect (l1 l2 acc)
              (cond ((or (null l1) (null l2)) (nreverse acc))
                    (t (let ((l1-element (funcall sort-key (car l1)))
                             (l2-element (funcall sort-key (car l2))))
                         (cond ((funcall sort-test l1-element l2-element)
                                (.isect (cdr l1) l2 acc))
                               ((funcall equality-test l1-element l2-element)
                                (.isect (cdr l1) (cdr l2) (cons (car l1) acc)))
                               (t (.isect l1 (cdr l2) acc))))))))
      (.isect lst-1-sorted lst-2-sorted '()))))

简单测试:

(isect '(0 1 2 3 4 5 6) '(9 0 3 5 12 24 8 6))
;; => (0 3 5 6)

(isect '(#\a #\c #\h #\t #\e #\r #\b #\a #\h #\n) 
       '(#\a #\m #\s #\e #\l #\s #\t #\a #\r) 
       :equality-test #'char= 
       :sort-test #'char< 
       :key #'id)
;; => (#\a #\a #\e #\r #\t)

(isect '("this" "is" "just" "a" "boring" "test")
       '("this" "boring" "strings" "are" "to" "be" "intersected")
       :equality-test #'string= 
       :sort-test #'string< 
       :key #'id)
;; => ("boring" "this")