这是sbcl中的一个错误?

问题描述 投票:5回答:2

为什么在sbcl中发生这种情况?也许是个bug?

(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 to:

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

问题消失了

我在sbcl 1.4.3和1.4.11中测试了这个。在clisp中似乎没有出现问题。

lisp common-lisp literals sbcl clos
2个回答
13
投票

不。您正在修改文字数据,这会产生不确定的后果。

当您在源代码中引用列表时,这意味着您要使用的内容正是读者从源代码生成的列表 - 这是一个文字列表。读者可能会记住这些事情,因此两个相同的列表不会重复。

解决此问题的一种方法是使用listcons在运行时创建列表:

(defclass myclass ()
  ((s1
    :initform (list (cons a 1) (cons b 2))) 
   (s2
    :initform (list (cons a 1) (cons b 2)))))

9
投票

这不是一个错误。 '((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))))))
© www.soinside.com 2019 - 2024. All rights reserved.