在 Common Lisp 中定义列表列表时出现重复列表

tsi*_*kov 1 compilation sbcl common-lisp

我将变量定义为包含符号列表的列表:

(defparameter *var* '((a a a) (a x a) (a a a)))
Run Code Online (Sandbox Code Playgroud)

setf当我尝试用...更改其中一个元素时

(setf (caar *var*) 'c) 
Run Code Online (Sandbox Code Playgroud)

...第一个和最后一个列表均已更新。

> *var*
; ((c a a) (a x a) (c a a))
Run Code Online (Sandbox Code Playgroud)

我注意到,当我defparameter在 REPL 中评估 时,该setf命令按预期工作。这让我认为意外的行为与编译过程有关。

问题:

  1. 发生了什么以及为什么?

  2. 定义包含相同符号的新列表的规范方法是什么defparameter?

我正在使用 SBCL。

编辑:我的问题与这个问题不相似,因为我不是问如何复制列表以便它们不共享结构,而是问为什么在编译defparameter具有相似元素的列表时似乎共享结构以及如何定义它们以便它们共享结构不是。

Rai*_*wig 7

这是一个常见问题。

为什么 SBCL 显示这种依赖于实现的行为?

加载编译文件后尝试(用于COMPILE-FILE编译它):

* (eq (first *var*) (third *var*))
T
* *var*
((A A A) (A X A) (A A A))
* (setf *print-circle* t)
T
* *var*
(#1=(A A A) (A X A) #1#)
* 
Run Code Online (Sandbox Code Playgroud)

上面显示第一个和第三个子列表是相同的。

在 REPL 中运行它作为练习。它可能会显示不同的结果。

在 ANSI Common Lisp 中,文件编译器可以合并 类似的列表数据。(a a a)并且(a a a)是相似的。这可以节省编译程序中的内存空间。并非每个 Common Lisp 实现都这样做,但 SBCL 做到了。请记住,当《Common Lisp 语言》第一版于 1984 年出版时,第一台 Apple Macintosh 只有 128KB RAM。

解决方案:使用COPY-TREE:

要创建嵌套列表的新的非合并副本,请使用该函数COPY-TREE。新鲜意味着它不是文字数据,因此允许修改它。COPY-TREE复制 cons 树的所有级别,COPY-LIST仅复制顶部列表。由于您的数据是嵌套的,我们需要COPY-TREE:

(defparameter *var*
  (copy-tree '((a a a) (a x a) (a a a))))
Run Code Online (Sandbox Code Playgroud)