In LISP, I have a function that is passed a list. I would like to change an element of this list without changing the original list. Normally, I would use copy-list
to create the local copy of the list which I will change, but this doesn't seem to be working:
CL-USER> (defun test (item)
(let ((copy (copy-list item)))
(setf (nth 0 (nth 0 (nth 0 copy))) t)
(print item)
(print copy)))
CL-USER> (defparameter item `(((NIL NIL) (NIL NIL) (NIL NIL))
((NIL NIL NIL) (NIL NIL NIL))
((3 3) (NIL NIL))))
CL-USER> (test item)
(((T NIL) (NIL NIL) (NIL NIL)) ((NIL NIL NIL) (NIL NIL NIL)) ((3 3) (NIL NIL)))
(((T NIL) (NIL NIL) (NIL NIL)) ((NIL NIL NIL) (NIL NIL NIL)) ((3 3) (NIL NIL)))
(((T NIL) (NIL NIL) (NIL NIL)) ((NIL NIL NIL) (NIL NIL NIL)) ((3 3) (NIL NIL)))
CL-USER> item
(((T NIL) (NIL NIL) (NIL NIL)) ((NIL NIL NIL) (NIL NIL NIL)) ((3 3) (NIL NIL)))
As you can see, the value of item
was changed by test
even though I copied the list into a local variable and changed the local copy. This seems to be a symptom of using nth
. If I use a single call of car
rather than the repeated call to nth
, the function works as expected and the item
is unchanged after the call.
Why does nth
behave like this and how can I continue to use nth
without altering the value passed to test
?
I am using Common Lisp.
Short answer: use cl:copy-tree
You probably want to copy the whole tree with copy-tree. The copy made by copy-list only generates new "backbone"; you get a new list, but with the same elements. Copy-tree will copy all the cons-tree structure that makes up the tree.
Long answer: list structure vs. tree structure
The background here is referenced in the documentation. From the HyperSpec:
That glossary entry for list structure is important:
As a very simple example, we can exploit the fact that *print-circle* will show us common substructure:
The HyperSpec entry on copy-tree doesn't link to tree structure, but there is a glossary entry:
That second sentence is a bit strange, but it's probably just in there as a minimally adjusted copy and paste of the list structure entry. See my answer to Definition of tree structure in Lisp for a bit more about that.