Elisp: Как удалить элемент из списка ассоциаций с помощью строкового ключа - PullRequest
16 голосов
/ 22 марта 2012

Теперь это работает просто отлично:

(setq al '((a . "1") (b . "2")))
(assq-delete-all 'a al)

Но я использую строки в качестве ключей в моем приложении:

(setq al '(("a" . "foo") ("b" . "bar")))

И это ничего не дает:

(assq-delete-all "a" al)

Я думаю, это потому, что экземпляр строкового объекта отличается (?)

Так как мне удалить элемент со строковым ключом из списка ассоциаций? Или я должен отказаться от использования символов в качестве ключей и при необходимости преобразовать их в строки?

Ответы [ 4 ]

17 голосов
/ 22 марта 2012

q в assq традиционно означает eq равенство, используемое для объектов.

Другими словами, assq - это eq ароматизированный assoc.

Строки не соответствуют eq равенству. Две строки, которые являются эквивалентными последовательностями символов, не могут быть eq. assoc в Emacs Lisp использует equal равенство, которое работает со строками.

Итак, вам нужен assoc-delete-all для вашего equal списка ассоциаций, но эта функция не существует.

Все, что я могу найти при поиске assoc-delete-all, это ветка списка рассылки: http://lists.gnu.org/archive/html/emacs-devel/2005-07/msg00169.html

Бросай свои. Это довольно тривиально: вы переходите по списку и собираете все эти записи в новый список, чей car не соответствует заданному ключу в equal.

Одной полезной вещью, на которую стоит обратить внимание, может быть библиотека совместимости Common Lisp. http://www.gnu.org/software/emacs/manual/html_node/cl/index.html

Там есть несколько полезных функций, таких как remove*, с помощью которых вы можете удалить из списка пользовательскую функцию предиката для тестирования элементов. С этим вы можете сделать что-то вроде этого:

;; remove "a" from al, using equal as the test, applied to the car of each element
(setq al (remove* "a" al :test 'equal :key 'car))

Деструктивный вариант delete*.

16 голосов
/ 22 марта 2012

Если вы знаете, что в вашем списке может быть только одна соответствующая запись, вы также можете использовать следующую форму:

(setq al (delq (assoc <string> al) al)

Обратите внимание, что setq (который отсутствовал в вашем примере кода) очень важен для операций `удаления 'в списках, в противном случае операция завершится неудачно, когда удаленный элемент окажется первым в списке.

4 голосов
/ 09 февраля 2019

Emacs 27+ включает в себя assoc-delete-all, который будет работать для строковых ключей, а также может использоваться с произвольными тестовыми функциями.

(assoc-delete-all KEY ALIST &optional TEST)

Delete from ALIST all elements whose car is KEY.
Compare keys with TEST.  Defaults to ‘equal’.
Return the modified alist.
Elements of ALIST that are not conses are ignored.

например:

(setf ALIST (assoc-delete-all KEY ALIST))

В более ранних версияхEmacs, cl-delete предоставляет альтернативу:

(setf ALIST (cl-delete KEY ALIST :key #'car :test #'equal))

, что эквивалентно говорит об удалении элементов из ALIST, где car элемента списка от equal до KEY.

nbВ ответе Kaz уже упоминается этот последний вариант, но с использованием более старых (require 'cl) имен delete* и remove*, тогда как вы теперь (для поддержки Emacs 24+) использовали бы cl-delete или cl-remove (которые являются автоматическими).-loaded).

2 голосов
/ 09 февраля 2019

Если вы используете emacs 25 или новее, вы можете использовать alist-get

(setf (alist-get "a" al t t 'equal) t)

...