Относятся ли списки свойств в Common Lisp к некоторому глобальному состоянию?

В приведенном ниже коде z используется как локальная переменная, но она ведет себя так, как если бы она была глобальной:

(defun foo (m)
  (let ((z '(stuff nil)))
    (push m (getf z 'stuff))
    (print z)))

(foo 1)
(foo 2)
(foo 3)

Я ожидаю, что результат будет

(STUFF (1)) 
(STUFF (2)) 
(STUFF (3)) 
T

но при запуске с SBCL я вижу

(STUFF (1)) 
(STUFF (2 1)) 
(STUFF (3 2 1)) 
T

Почему это так? Это поведение характерно для списков свойств?


person carlo_hamalainen    schedule 08.01.2011    source источник
comment
Возможный дубликат Неожиданное сохранение данных   -  person Joshua Taylor    schedule 23.10.2015


Ответы (1)


В foo z связано с литеральным выражением '(stuff nil). Функция деструктивно изменяет z, тем самым деструктивно изменяя значение литерала. Как LISP ведет себя в подобных обстоятельствах, зависит от реализации. Некоторые реализации будут послушно изменять буквальное значение (как в вашем случае). Другие реализации помещают литералы в ячейки памяти, доступные только для чтения, и если вы попытаетесь изменить эти литералы, произойдет сбой.

Чтобы получить желаемое поведение, используйте COPY-LIST, чтобы сделать копию литерала, которую можно безопасно изменить:

(defun foo (m)
  (let ((z (copy-list '(stuff nil))))
    (push m (getf z 'stuff))
    (print z)))
person WReach    schedule 08.01.2011
comment
Я думаю, что более идиоматично было бы использовать LIST, как в (let ((z (list 'stuff nil))) ...) - person Ken; 08.01.2011