Lisp:两个列表的有序并集

Lisp:两个列表的有序并集,lisp,common-lisp,union,Lisp,Common Lisp,Union,我试图创建一个函数,以有序的方式返回两个列表的并集。 这是我的密码: (defun setunion (lst1 lst2) (cond ((null lst1) lst2) ((null lst2) lst1) ((member (car lst2) lst1) (setunion lst1 (cdr lst2))) (t (append (setunion lst1 (cdr lst2)) (list (car ls

我试图创建一个函数,以有序的方式返回两个列表的并集。 这是我的密码:

(defun setunion (lst1 lst2)
  (cond
    ((null lst1) lst2)
    ((null lst2) lst1)
    ((member (car lst2) lst1)
     (setunion lst1 (cdr lst2)))
    (t (append (setunion lst1 (cdr lst2))
               (list (car lst2))))))


(print (setunion '(a b c) '(a c d e f a)))
它返回(abcfed),但我要查找的输出是(abcdef)。如何更改代码以返回正确的输出

谢谢


编辑:我想我已经弄明白了。我创建了一个helper函数,它删除列表2的重复项,并将其反转,同时删除列表1的重复项

(defun help (lst1 lst2)
(setunion (remove-duplicates lst1 :from-end t) (reverse(remove-duplicates lst2 :from-end t))))
(print (help  '(b c b d) '(a d e a)))

这给了我一个输出(B C D A E),这就是我要寻找的。

好的,所以基本上你要做的就是删除所有列表中的重复项,元素应该按照第一次出现的顺序排列。您可以附加所有列表,然后从末尾删除重复项:

(defun set-union (&rest lists)
  (remove-duplicates (reduce #'append lists)
                     :from-end t))

如果您想要的是一组列表的并集,使得列表中的元素按照它们在列表中出现的顺序出现,从左侧开始工作,那么这里有一种非常自然的方法。我不确定这是否是我在现实生活中所写的。它的优点是:

  • 很容易看到发生了什么
  • 它不依赖于毛茸茸的标准CL函数
它的缺点是需要消除尾部调用才能处理长列表(有些人认为这样工作的代码不是惯用代码)

这里有一个版本,它使用显式迭代,但在其他方面也使用相同的技巧

(defun union-preserving-order (&rest ls)
  ;; Union of a bunch of lists.  The result will not contain
  ;; duplicates (under EQL) and elements will occur in the order they
  ;; occur in the lists, working from the left to the right.  So
  ;; (union-preserving-order '(a b) '(b a c)) will be (a b c), as will
  ;; (union-preserving-order '(a b) '(c b a)), while
  ;; (union-preserving-order '(b a) '(a b c) '(c d)) will be (b a c
  ;; d).
  (let ((accum '()))
    (dolist (l ls (nreverse accum))
      (dolist (e l)
        (pushnew e accum)))))
最后,这里是一个肮脏的黑客建立的结果转发。在没有证据的情况下,我认为这在性能方面是最好的,而无需借助一些聪明的查找结构(如哈希表)来检查您是否已经看到了元素

(defun union-preserving-order (&rest ls)
  ;; Union of a bunch of lists.  The result will not contain
  ;; duplicates (under EQL) and elements will occur in the order they
  ;; occur in the lists, working from the left to the right.  So
  ;; (union-preserving-order '(a b) '(b a c)) will be (a b c), as will
  ;; (union-preserving-order '(a b) '(c b a)), while
  ;; (union-preserving-order '(b a) '(a b c) '(c d)) will be (b a c
  ;; d).
  (let ((results '())                   ;results we'll return
        (rlc nil))                      ;last cons of results
    (dolist (l ls results)
      (dolist (e l)
        (unless (member e results)
          (if (not (null rlc))
              (setf (cdr rlc) (list e)
                    rlc (cdr rlc))
            (setf rlc (list e)
                  results rlc)))))))

示例中的第二个列表没有排序。如果您想要有序的输出,您需要以某种特定的方式添加它们或对结果进行排序。操作可能需要查看比较函数、“合并”、“排序”或类似操作。请注意,在末尾附加单个元素效率非常低,并且会颠倒附加元素的顺序。第二个列表中有重复的元素。
(setunion'(bcd)'(adea))的输出应该是什么?如果你想要一个有序的对象,你必须有一个定义顺序的函数,而你没有。setunion'(bcd)'(adea))的输出应该是(bcae)
(defun union-preserving-order (&rest ls)
  ;; Union of a bunch of lists.  The result will not contain
  ;; duplicates (under EQL) and elements will occur in the order they
  ;; occur in the lists, working from the left to the right.  So
  ;; (union-preserving-order '(a b) '(b a c)) will be (a b c), as will
  ;; (union-preserving-order '(a b) '(c b a)), while
  ;; (union-preserving-order '(b a) '(a b c) '(c d)) will be (b a c
  ;; d).
  (let ((results '())                   ;results we'll return
        (rlc nil))                      ;last cons of results
    (dolist (l ls results)
      (dolist (e l)
        (unless (member e results)
          (if (not (null rlc))
              (setf (cdr rlc) (list e)
                    rlc (cdr rlc))
            (setf rlc (list e)
                  results rlc)))))))