Elisp:如何查找列表重复项

Elisp: how to find list duplicates

我正在使用它来查找列表重复项:

(defun have-dups (x)
  (let ((dups (copy-tree x)))
    (if (eq (length (delete-dups dups)) (length x))
    nil
      t)))

(have-dups (list 1 2 3 3)) ;=> t
(have-dups (list 1 2 3))   ;=> nil 

考虑到 copy-treedelete-dups 的开销,可能有更好的方法。

使用散列 table,只要您发现散列 table 中已经存在的元素,您就知道您有重复项:

(defun has-dup (list)
  (block nil
    (let ((hash (make-hash-table :test 'eql)))
      (map ()
           (lambda (item)
             (if (gethash item hash)
                 (return t)
               (setf (gethash item hash) t)))
           list))))

这是您的答案的简短版本,它使用 remove-duplicates 而不是 delete-dups,以避免后者的破坏性:

(defun has-dups-p (LIST) ""
       (let ((unique1 (remove-duplicates LIST :test #'equal)))
     (if (eq LIST unique1)
         nil
       t)))
(has-dups '(1 2 3 2 1)) ; t
(has-dups '("a" "b" "c")) ; nil

我发现这相当容易阅读,并且 eq 应该相当有效,因为它直接进入 C,特别是在列表早期出现重复的情况下。 remove-duplicatesdelete-dups 都传递给 cl--delete-duplicates,这相当复杂...

随后是更长的解决方案,returns LIST 的元素有重复项,每个 duplicated[=] 的 position LIST 中的 35=] 元素(记住 seq 在 elisp 中是零索引的)。请注意,这目前仅适用于 LIST 的元素是 string 的情况,尽管我确信它可以进一步扩展到更一般的情况:

(defun list-duplicates (LIST) "
Returns `nil' when LIST has no duplicates.
Otherise, returns a `list' of `cons's.
In each list element:
- the `car' is the element of LIST which has duplicates.
- the `cdr' is a list of the positions where the duplicates are found."
   (interactive)
   ;; res1 = result
   ;; unique1 = LIST with duplicates removed
   (let ((unique1 (remove-duplicates LIST :test #'string-equal))
     (res1 '()))
     (if (eq LIST unique1)
     nil
       (progn
     (dolist (x unique1)
       ;; i = incrementor
       ;; pos1 = list of postions of duplicates
       (let (y (i 0) (pos1 '()))
         (while (member x LIST)
           (set 'y (seq-position LIST x))
           (when (> i 0)
         (push y pos1))
           (set 'i (+ 1 i))
           (set 'LIST
            (substitute (concat x "1") x LIST :test #'string-equal :count 1)))
         (push (cons x (nreverse pos1)) res1)))
     (nreverse res1)))))

例如

(list-duplicates '("a" "b" "c")) ; nil
(list-duplicates '("a" "b" "b" "a" "b" "c" "c")) ; (("a" 3) ("b" 2 4) ("c" 6))