Это ошибка в sbcl?

Почему это происходит в sbcl? Может баг?

(defclass myclass ()
  ((s1
    :initform '((a . 1) (b . 2))) 
   (s2
    :initform '((a . 1) (b . 2)))))

(defparameter ins (make-instance 'myclass))

(setf (cdr (assoc 'a (slot-value ins 's1))) 43) ;; change only slot s1

;; here my problem

(slot-value ins 's1)  ;; => ((a . 44) (b . 2)))
(slot-value ins 's2)  ;; => ((a . 44) (b . 2)))

Но если изменить :initform на:

(defclass myclass ()
  ((s1
    :initform '((a . 1) (b . 2))) 
   (s2
    :initform '((a . 1) (b . 3)))))

Проблема исчезает

Я тестировал это в sbcl 1.4.3 и 1.4.11. В clisp вроде проблем не возникает.


person Marcelo Muñoz    schedule 29.08.2018    source источник
comment
Попробуйте: (eq (slot-value ins 's1) (slot-value ins 's2)) и обратите внимание, что он возвращает.   -  person Barmar    schedule 30.08.2018
comment
Допускается оптимизация пространства данных статического списка.   -  person Rainer Joswig    schedule 30.08.2018


Ответы (2)


Нет. Вы изменяете буквальные данные, что имеет неопределенные последствия.

Когда вы цитируете список в исходном коде, это означает, что вещь, с которой вы хотите работать, — это именно тот список, который читатель создал из вашего исходного кода — это буквальный список. Такие вещи читатель может запомнить, чтобы два одинаковых списка не дублировались.

Один из способов исправить это — создать списки во время выполнения, используя list и cons:

(defclass myclass ()
  ((s1
    :initform (list (cons a 1) (cons b 2))) 
   (s2
    :initform (list (cons a 1) (cons b 2)))))
person Svante    schedule 29.08.2018
comment
Я понял, спасибо за помощь!!, я недавно начал изучать CL, и я все еще работаю, чтобы понять некоторые вещи. - person Marcelo Muñoz; 30.08.2018

Это не ошибка. '((a . 1) (b . 2)) является буквальной константой и, как и все константы, считается неизменной. Это означает, что все вхождения '(a . 1), которые также являются буквальными, могут просто указывать на car другого, поскольку оно никогда не должно меняться.

Теперь реализации могут выбрать создание новых структур, поэтому CLISP может сделать это, но вы не можете полагаться на это. Вы не должны изменять буквальные данные.

Если вы собираетесь изменить его, вам нужно использовать глубокую копию, например:

(defclass myclass ()
  ((s1
    :initform (copy-tree '((a . 1) (b . 2)))) 
   (s2
    :initform (copy-tree '((a . 1) (b . 2))))))
person Sylwester    schedule 29.08.2018
comment
Другая проблема заключается в том, что даже если эти два списка ассоциированных объектов различаются, они не создаются заново для каждого нового экземпляра myclass. - person Kaz; 31.08.2018
comment
@Kaz Верно, но copy-tree все еще лекарство :-) - person Sylwester; 31.08.2018
comment
copy-alist легче для желудка и применим здесь, хотя и не такой широкий антибиотик. - person Kaz; 31.08.2018