【问题标题】:Elisp: how to find list duplicatesElisp:如何查找列表重复项
【发布时间】:2018-02-27 09:46:00
【问题描述】:

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

(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的开销,或许有更好的办法。

【问题讨论】:

    标签: list duplicates lisp elisp


    【解决方案1】:

    使用哈希表,一旦你找到一个已经存在于哈希表中的元素,你就知道你有重复:

    (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))))
    

    【讨论】:

    • 你为什么使用(block nil (...)) wrapper,是不是可以只调用return 而不是return-from has-dup?另外,为什么您在最后一行包含nil - 没有它,代码似乎也可以工作?抱歉,我刚接触 lisp。
    • @AntonMalyshev 在 elisp 中 defun 本身不会建立块。最后的nil 似乎没有必要(因为(map () ...) 无论如何都会返回nil),但我认为它是为了便于阅读。
    • @AntonMalyshev 正如 jkiiski 所说。如果需要,您可以删除最后一个 nil
    【解决方案2】:

    这是您答案的较短版本,它使用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 这相当复杂......

    下面是一个更长的解决方案,它返回 LIST 中具有重复项的元素以及 LIST 中每个 duplicated 元素的 位置(请记住 seq s 在 elisp 中是零索引的)。请注意,这目前仅适用于 LIST 的元素为 strings 的情况,尽管我确信它可以进一步扩展到更一般的情况:

    (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))
    

    【讨论】:

      猜你喜欢
      • 2014-09-03
      • 1970-01-01
      • 2013-03-29
      • 2013-02-21
      • 2013-11-17
      • 2018-11-07
      • 1970-01-01
      • 2021-04-05
      相关资源
      最近更新 更多