我正在学习Lisp。我希望在列表列表中添加一个新列表,比如说((1 1 1)(0 0 0)),其中此列表集合的新头部是根据前一个头部计算的。
这是我尝试过的,在Slimv的REPL环境中使用sbcl:
> (defvar *ll* (list (list 1 1 1) (list 0 0 0)))
*LL*
> *ll*
((1 1 1) (0 0 0))
> (push (car *ll*) *ll*)
((1 1 1) (1 1 1) (0 0 0))
> (setf (nth 2 (car *ll*)) 2)
2
> *ll*
((1 1 2) (1 1 2) (0 0 0))
如上所示,我只想修改第一个列表的最后一个元素,但不知何故,第二个列表的最后一个元素也被修改了。
我注意到,如果我推入一个全新的列表,那么结果就不同了:
> (defvar *lll* (list (list 1 1 1) (list 0 0 0)))
*LLL*
> (push '(1 1 1) *lll*)
((1 1 1) (1 1 1) (0 0 0))
> (setf (nth 2 (car *lll*)) 2)
2
> *lll*
((1 1 2) (1 1 1) (0 0 0))
我想知道导致这些不同结果的原因,以及如何实现"将新列表添加到列表列表的结果,其中结果列表集合的新头部基于计算以前的头。"谢谢!
答案 0 :(得分:1)
(car *ll*)
和(cadr *ll*)
是相同的列表> (defvar *ll* (list (list 1 1 1) (list 0 0 0))) *LL* > *ll* ((1 1 1) (0 0 0)) > (push (car *ll*) *ll*) ((1 1 1) (1 1 1) (0 0 0)) > (setf (nth 2 (car *ll*)) 2) 2 > *ll* ((1 1 2) (1 1 2) (0 0 0))
如上所示,我只想修改第一个元素 列表,但不知何故,第二个列表的最后一个元素也是 改变。
那里只有一个对象,你修改了它。它与你有某种结构化数据类型(实际上,什么是cons单元格,只有两个字段的结构化数据类型)并没有什么不同。如果您有一个人员列表,然后再将第一个人添加到列表中,那么仍然只有一个人;这个人只出现在名单中的两个地方。如果您更改此人的姓名,您将在两个地方都看到它。如果您将*print-circle*
设置为t
,则实际上可以看到共享结构。
CL-USER> (defvar *ll* (list (list 1 1 1) (list 0 0 0)))
*LL*
CL-USER> *ll*
((1 1 1) (0 0 0))
CL-USER> (push (car *ll*) *ll*)
((1 1 1) (1 1 1) (0 0 0))
CL-USER> *ll*
((1 1 1) (1 1 1) (0 0 0))
CL-USER> (setf *print-circle* t)
T
CL-USER> *ll*
(#1=(1 1 1) #1# (0 0 0))
使用#1=…
和#1#
的表示法表示相同的对象是列表的第一个和第二个元素。
我希望在列表列表中添加一个新列表,比如
((1 1 1) (0 0 0))
, 这个列表集合的新头部是基于的 前任主管。 ...> (push (car *ll*) *ll*) ((1 1 1) (1 1 1) (0 0 0))
您说要将新列表添加到列表列表中,但是您没有添加新列表;您正在添加(car *ll*)
,这是您在(list 1 1 1)
开头创建的列表。如果您要复制列表,则需要明确复制,例如copy-list
:
> (push (copy-list (car *ll*)) *ll*)
((1 1 1) (1 1 1) (0 0 0))
另外,您在第二个代码块中执行的操作实际上是未定义的行为,因为您正在修改文字列表'(1 1 1)
。
> (defvar *lll* (list (list 1 1 1) (list 0 0 0))) *LLL* > (push '(1 1 1) *lll*) ; '(1 1 1) is literal data. ((1 1 1) (1 1 1) (0 0 0)) > (setf (nth 2 (car *lll*)) 2) ; (car *lll*) is literal data, and you're modifying it! 2 > *lll* ((1 1 2) (1 1 1) (0 0 0))
请参阅my answer至Unexpected persistence of data了解更多有关此问题的原因。