Báo cáo trùng lặp trong một danh sách?


7

Q: làm thế nào để tôi chỉ nhận được các yếu tố trùng lặp trong một danh sách?

delete-dups(và cl-delete-duplicates) xóa tất cả các thành phần trùng lặp khỏi danh sách:

(delete-dups '(a b c c d d))            ; => '(a b c d)

Tôi muốn điều ngược lại: có một hàm chỉ trả về các bản sao trong danh sách không?

(mystery-function '(a b c c d d))       ; => '(c d)

Câu trả lời:


6

Tôi nghĩ cách dễ nhất là sử dụng bảng băm :

(defun get-duplicates (list &optional test)
  (let ((ht (make-hash-table :test (or test #'equal))) 
        ret)
    (dolist (x list)
      (incf (gethash x ht 0)))
    (maphash (lambda (key value)
               (when (> value 1)
                 (push key ret)))
             ht)
    ret))
(get-duplicates '(a 2 a b 3 2))
==> (2 a)

Fwiw có một phiên bản được đánh dấu sao của defun chấp nhận đối số từ khóa.
— YoungFrog

6

Sử dụng dấu gạch ngang:

(defun find-duplicates (list)
  "Return a list that contains each element from LIST that occurs more than once."
  (--> list
       (-group-by #'identity it)
       (-filter (lambda (ele) (> (length ele) 2)) it)
       (mapcar #'car it)))

Bộ kiểm tra nhanh:

(ert-deftest nothing ()
  (should-not (find-duplicates '())))

(ert-deftest no-duplicates ()
  (should-not (find-duplicates '(1 2 3 4 5 6 7 "eight"))))

(ert-deftest single-duplicate ()
  (should (equal (find-duplicates '(1 2 3 4 1))
                 '(1))))

(ert-deftest multiple-duplicates ()
  (should (equal (sort (find-duplicates '(1 2 3 4 1 6 7 8 9 2))
                       #'<)
                 '(1 2))))

(ert-deftest string-duplicates ()
  (should (equal (find-duplicates '(1 2 "three" 4 "three"))
                 '("three"))))

Hiện tại nó dường như trả lại các mục theo thứ tự xuất hiện đầu tiên của mỗi bản sao, nhưng tôi không thấy bất cứ điều gì -group-byđảm bảo điều đó, vì vậy tôi không nghĩ rằng có thể dựa vào. Nó có thể có thể hiệu quả hơn, sử dụng hashtables, nhưng điều này hoạt động.


3

Đây là phiên bản không băm:

#+BEGIN_SRC emacs-lisp
(defun find-duplicates (list)
  (loop for (item . count) in (let ((counts '())
                    place)
                (dolist (el list)
                  (setq place (assoc el counts))
                  (if place
                      (incf (cdr place))
                    (push (cons el 1) counts)))
                counts)
    if (> count 1)
    collect item))
#+END_SRC

0

Đảo ngược delete-dupsbằng ... delete-dups(và seq):

(defun report-dups (list)
  (delete-dups (seq-filter
                (lambda (el) (member el (cdr (member el list))))
                list)))

0

Điều này tương tự với định nghĩa của @ caseneuve.

(defun report-dups (xs)
  (delete-dups (cl-remove-if-not (lambda (x) (member x (cdr (member x xs)))) xs)))

Nhưng cả hai đều chịu sự kiểm tra từng yếu tố trong danh sách, ngay cả khi nó đã được thử nghiệm. Và rồi họ chạy delete-dups.

Định nghĩa này rất đơn giản và nó không bị những sự thiếu hiệu quả đó:

(defun report-dups (xs)
  (let ((ys  ()))
    (while xs
      (unless (member (car xs) ys) ; Don't check it if already known to be a dup.
        (when (member (car xs) (cdr xs)) (push (car xs) ys)))
      (setq xs  (cdr xs)))
    ys))

Nó dường như cũng nhanh hơn khoảng 6 lần so với giải pháp bảng băm ( get-duplicates, ở trên).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.