Почему константа в `let` изменяется при повторных вызовах?

Скажем, у нас есть такая функция foo :

(defun foo (e)
  (let ((lst '(a b c)))
    (delq e lst)))

Затем мы используем его следующим образом (последовательно оценивая один за другим):

(foo 'c) ; => (a b)
(foo 'b) ; => (a)
(foo 'a) ; => nil
(foo 'b) ; => (a)

Что здесь случилось?

Если я определяю другую функцию bar :

(defun bar (e)
  (let ((lst (list 'a 'b 'c)))
    (delq e lst)))

Затем используйте его так же:

(bar 'c) ; => (a b)
(bar 'b) ; => (a c)
(bar 'a) ; => (b c)
(bar 'b) ; => (a c)

Результаты здесь кажутся мне разумными.

Итак, вопрос в том, почему константа в форме let функции foo ведет себя таким образом? Это предназначено?

11
добавлено автор shabbychef, источник
Другая ключевая цитата, выделенная в дубликате на S.O. это (от Ch f quote ): «Предупреждение: quote не создает его возвращаемое значение, а просто возвращает значение, которое было предварительно построено читателем Lisp ». Я думаю, что новички в lisp часто не обращают внимания на всю концепцию читателя-читателя, поэтому, чтобы усвоить и понять различие между этапами «читать» и «eval», очень полезно для понимания языка в целом, поскольку а также конкретные проблемы, подобные выше.
добавлено автор Mark Ireland, источник
Строго говоря ' не является макросом читателя в elisp (у elisp нет макросов читателя), но он, безусловно, обрабатывается во время фазы чтения. Впрочем, это не главное. Моя мысль заключалась в понимании того, что читатель преобразует весь текстовый код Lisp в объекты Lisp (один раз), и именно те объекты, которые впоследствии eval'd (часто неоднократно), а не текст, который вы видите.
добавлено автор Mark Ireland, источник
И поскольку объекты могут подвергаться манипуляциям во время оценки (как в этом вопросе), то, что оценивается в любой момент времени, не нуждается для представления кода, который был первоначально прочитан. Конечно, в большинстве случаев он будет делать именно это - вам не часто приходится думать с точки зрения объектов. Но знание того, что происходит за кулисами, позволяет вам распознавать и понимать ситуации, в которых такая вещь является фактором.
добавлено автор Mark Ireland, источник
@phils Вы имеете в виду, что ' - это макрос читателя, который обрабатывается на этапе «читать», а (список «a» b »c) оценивается в разделе« eval "?
добавлено автор Mahesh, источник
@phils Спасибо! В этом есть смысл.
добавлено автор Mahesh, источник

1 ответы

Вот мой ответ на идентичный вопрос , соответствующим образом отредактированный:

Плохо

foo is self-modifying code. This is extremely dangerous. While the variable lst disappears at the end of the let form, its initial value persists in the function object, and that is the value you are modifying. Remember that in Lisp a function is a first class object, which can be passed around (just like a number or a list), and, sometimes, modified. This is exactly what you are doing here: the initial value for lst is a part of the function object and you are modifying it.

Давайте фактически посмотрим, что происходит:

(symbol-function 'foo)
==> (lambda (e) (let ((lst (quote (a b c)))) (delq e lst)))
(foo 'c)
==> (a b)
(symbol-function 'foo)
==> (lambda (e) (let ((lst (quote (a b)))) (delq e lst)))
(foo 'b)
==> (a)
(symbol-function 'foo)
==> (lambda (e) (let ((lst (quote (a)))) (delq e lst)))
(foo 'a)
==> nil
(symbol-function 'foo)
==> (lambda (e) (let ((lst (quote (a)))) (delq e lst)))

Добро

В bar , lst инициализируется ячейкой cons fresh и, следовательно, является безопасной. bar делает not свой код.

Нижняя линия

В целом, лучше всего обработать данные например '(1) в качестве констант - do not изменить их:

quote возвращает аргумент, не оценивая его. (quote x) дает x .    Предупреждение : quote не создает его возвращаемое значение, а просто возвращает   значение, которое было предварительно построено читателем Lisp (см. информационный узел    Печатное представление ). Это означает, что (a. B) не является   идентичный (cons 'a' b) : первый не противоречит. Цитирование должно   зарезервированы для констант, которые никогда не будут изменены побочными эффектами,   если вам не нравится самомодифицирующийся код. См. Общую ошибку в информации   узел Перегруппировка для примера неожиданных результатов когда   изменен цитируемый объект.

Если вам нужно изменить список , создайте его с помощью list или cons или copy-list вместо quote ), как в bar .

См. подробнее примеры .

ps - имя переменной

Emacs Lisp - это lisp-2 , поэтому нет причин называть вашу переменную lst вместо список .

18
добавлено
да, это очень смущает! Но также очень интересно, я понятия не имел о lisp-1 над lisp-2.
добавлено автор Jeff, источник
Спасибо за подробное объяснение! Кроме того, я знаю, что ELisp - это lisp-2, но все же я не хочу путать себя. Что-то вроде (список (список 'a' b 'c)) (хотя он находится в форме let ) выглядит смутно с первого взгляда.
добавлено автор Mahesh, источник
Верстка сайтов HTML/CSS/JS/PHP
Верстка сайтов HTML/CSS/JS/PHP
3 439 участник(ов)

Правила группы: напишите !rules в чате. Группа Вк: vk.com/web_structure Freelancer: @web_fl Веб Дизайн: @dev_design Маркетолог: @topmarkening Автор: @M_Boroda

CSS — русскоговорящее сообщество
CSS — русскоговорящее сообщество
1 502 участник(ов)

Сообщество любителей CSS Возникли проблемы с CSS? – пиши сюда, обсудим и предложим самое лучшее решение Работа: @css_ru_jobs Правила: https://teletype.in/@css_ru/r1EWtQ2w7 Приходите в наши чаты @javascript_ru и @frontend_ru Флуд: @css_flood

Чат — Типичный Верстальщик
Чат — Типичный Верстальщик
1 080 участник(ов)

Основной канал: @tpverstak Обратная связь: @annblok Все ссылки на соц.сети проекта: http://taplink.cc/tpverstak ПРАВИЛА ЧАТА — https://teletype.in/@annblok/BygPgC3E7

Веб-Технологи: UI/UX, Вёрстка, Фронтенд
Веб-Технологи: UI/UX, Вёрстка, Фронтенд
167 участник(ов)

Всё про веб-дизайн и вёрстку. А также: HTML, CSS, флекс и бутстрапы, шаблонизаторы, препроцессоры, методологии, аглифаеры, улучшаторы и обфускаторы. Обсуждаем темы юзабилити, устраиваем А/В тесты лендингов, и проводим аудит.

DTP :: @DTPublish
DTP :: @DTPublish
147 участник(ов)

Обсуждаемые темы: полиграфия, препресс, верстка, дизайн, иллюстрации, скрипты, плагины. Канал - @DTPublishing

css_jobs
css_jobs
26 участник(ов)

Чат для вопросов по css и html: @css_ru Флуд: @css_flood Канал с вакансиями и резюме: @css_jobs_feed

css_флуд
css_флуд
10 участник(ов)