Удаление дублирующихся элементов в списке строк в elisp

Учитывая список, такой как

(list "foo" "bar" nil "moo" "bar" "moo" nil "affe")

как мне создать новый список с удаленными дублирующимися строками, а также nilS раздели, т.е.

(list "foo" "bar" "moo" "affe")

Порядок элементов должен быть сохранен - ​​первое вхождение строки не может быть удалено.

Списки, с которыми я здесь работаю, короткие, поэтому нет необходимости использовать что-либо вроде хеш-таблицы для проверки уникальности, хотя это, безусловно, не повредит. Однако, используя cl функциональность не является жизнеспособным вариантом.

5 ответов

Решение

Вот, пожалуйста.

(defun strip-duplicates (list)
  (let ((new-list nil))
    (while list
      (when (and (car list) (not (member (car list) new-list)))
        (setq new-list (cons (car list) new-list)))
      (setq list (cdr list)))
    (nreverse new-list)))

Попробуйте "Наборы и списки" в разделе "Списки" Справочного руководства по Emacs Lisp:

(delq nil (delete-dups (list "foo" "bar" nil "moo" "bar" "moo" nil "affe")))

Пакет Common Lisp содержит много функций управления списками, в частности remove-duplicates,

(require 'cl)
(remove-duplicates (list "foo" "bar" nil "moo" "bar" "moo" nil "affe")
                   :test (lambda (x y) (or (null y) (equal x y)))
                   :from-end t)

Да, я понимаю, ты сказал, что не хочешь использовать cl, Но я все еще упоминаю это как правильный способ сделать это для других людей, которые могут прочитать эту ветку.

(Почему cl в любом случае не подходит для вас? Он поставляется с Emacs уже около 20 лет, не считая менее показанных прошлых воплощений.)

Если вы используете dash.el библиотека, это все, что вам нужно:

(-distinct (-non-nil '(1 1 nil 2 2 nil 3)) ; => (1 2 3)

dash.el написана Магнаром Свином, и это отличная библиотека для манипулирования списками с множеством функций для самых разных задач. Я рекомендую установить его, если вы пишете много кода Elisp. функция -distinct удаляет дубликаты элементов в списке, -non-nil удаляет nil элементы. Хотя приведенного выше кода достаточно, ниже я опишу альтернативный подход, поэтому не стесняйтесь игнорировать остальную часть поста.

-non-nil был добавлен в версии 2.9, поэтому, если по какой-то причине вам придется использовать более ранние версии, другой способ добиться того же - использовать -keep со встроенным identity функция, которая просто возвращает то, что ей дано: (identity 1) ; => 1, Идея в том, что -keep содержит только элементы, для которых предикат возвращает true ("не ноль" на жаргоне Lisp). identity очевидно, возвращает ненулевое значение только для тех значений, которые не равны нулю:

(-distinct (-keep 'identity '(1 1 nil 2 2 nil 3)) ; => (1 2 3)

Это краткий пример:

      (delete-duplicates '("~/.emacs.d" "~/.emacs.d") :test #'string-equal) ;; '("~/emacs.d")

В основном вы используете :testключевое слово для выбора функции string-equalчтобы проверить, дублируются ли элементы.

В противном случае проверка функции по умолчанию не проверяет равенство строк.

Другие вопросы по тегам