Elisp — Удалить единственный элемент в списке длиной 1

#emacs #lisp #elisp

#emacs #lisp #elisp

Вопрос:

В Emacs Lisp кажется, что я не могу удалить последний элемент из списка.

 ;; This works.
(setq list1 '(alpha beta))
(delete 'alpha list1) ;; => (beta)
;; list1 => (beta)

;; NOPE.
(setq list1 '(alpha))
(delete 'alpha list1) ;; => nil
;; list1 => (alpha)
  

Почему?

Также, если это возможно сделать, как мне это сделать?

Ответ №1:

Хотя delete для удаления элемента списка используются побочные эффекты, это не гарантировано. Правильный способ его использования — присвоить результат обратно переменной:

 (setq list1 (delete 'alpha list1))
  

Фактически, когда я пробую ваш первый пример, list1 также остается неизменным:

 ELISP> (setq list1 '(alpha beta))
(alpha beta)
ELISP> (delete 'alpha list1)
(beta)
ELISP> list1
(alpha beta)
  

Это потому, что delete это функция, а не макрос или специальная форма. Это означает, что он не может изменить привязку переменной list1 ; он может изменить только содержимое ячейки cons, на которую list1 указывает. Если элемент списка, который должен быть удален, является первым элементом в списке, самый простой способ удалить его — просто вернуть конец (он же cdr) списка. Если удаляемый элемент является единственным элементом в списке, правильным возвращаемым значением будет nil (пустой список), и нет способа изменить ячейку cons, чтобы она стала nil .

Вы заметите, что это действительно работает, если вы удалите второй элемент:

 ELISP> (setq list1 '(alpha beta))
(alpha beta)
ELISP> (delete 'beta list1)
(alpha)
ELISP> list1
(alpha)
  

В этом случае delete заканчивается выполнение (setcdr list1 nil) , которое требует изменения только ячейки cons, а не привязки list1 .

Комментарии:

1. Я понял свой первый пример совершенно неправильно. Спасибо за подробное объяснение. На «удаление использует побочные эффекты», вы подразумеваете, что я должен делать это по-другому?

2. Нет, это просто для того, чтобы объяснить, почему это иногда работает, а иногда нет. remove выполняет то же самое, что и delete , за исключением того, что не использует побочные эффекты для изменения аргумента, но возвращает копию списка с удаленными соответствующими элементами, так что без этого он никогда бы не работал setq .

Ответ №2:

От C-h v delete :

Напишите (setq foo (delete element foo))' to be sure of correctly
changing the value of a sequence
foo’.

И, по-видимому, '() вычисляется как nil .

Как это сделать тогда:

 (setq list1 '(alpha))
(setq list1 (delete 'alpha list1))