Ответ 1
Плохой
test-a
является самомодифицирующимся кодом. Это чрезвычайно опасно. Пока переменная x
исчезает в конце формы let
, ее начальное значение сохраняется в объекте функции, и это значение, которое вы изменяете. Помните, что в Lisp функция является объектом первого класса, который может быть передан (точно так же, как число или список), и, иногда, модифицировано. Это именно то, что вы здесь делаете: начальное значение для x
является частью объекта функции и вы его изменяете.
Давайте посмотрим, что происходит:
(symbol-function 'test-a)
=> (lambda nil (let ((x (quote (nil)))) (setcar x (cons 1 (car x))) x))
(test-a)
=> ((1))
(symbol-function 'test-a)
=> (lambda nil (let ((x (quote ((1))))) (setcar x (cons 1 (car x))) x))
(test-a)
=> ((1 1))
(symbol-function 'test-a)
=> (lambda nil (let ((x (quote ((1 1))))) (setcar x (cons 1 (car x))) x))
(test-a)
=> ((1 1 1))
(symbol-function 'test-a)
=> (lambda nil (let ((x (quote ((1 1 1))))) (setcar x (cons 1 (car x))) x))
Хороший
test-b
возвращает новую свежую ячейку и, таким образом, безопасен. Начальное значение x
никогда не изменяется. Разница между (setcar x ...)
и (setq x ...)
заключается в том, что первая модифицирует объект, уже сохраненный в переменной x
, в то время как последний сохраняет a новый strong > объект в x
. Разница аналогична x.setField(42)
vs. x = new MyObject(42)
в C++
.
Нижняя линия
В общем, лучше всего обрабатывать данные, такие как '(1)
как константы - do not изменить их:
quote
возвращает аргумент, не оценивая его.(quote x)
даетx
. Предупреждение:quote
не создает возвращаемое значение, а просто возвращает значение, которое было предварительно построено читателем Lisp (см. информацию node Печатное представление). Это означает, что(a . b)
не идентичный(cons 'a 'b)
: первый не противоречит. Цитирование должно зарезервированы для констант, которые никогда не будут изменены побочными эффектами, если вам не нравится самомодифицирующийся код. См. Общую ошибку в информации node Rearrangement для примера неожиданных результатов, когда изменен цитируемый объект.
Если вам нужно изменить список, создайте его с помощью list
или cons
или copy-list
вместо quote
.
PS. Это было дублировано на Emacs.
ПФС. См. Также Почему эта функция возвращает разные значения каждый раз? для одинаковой общей проблемы Lisp.