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 cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.