I'm new to common lisp, so hope someone would clarify this to me:
say we have a list and want to add an item with push
to modify it:
CL-USER> (defparameter xx '(1 2 3))
XX
CL-USER> xx
(1 2 3)
CL-USER> (push 100 xx)
(100 1 2 3)
CL-USER> xx
(100 1 2 3)
as expected. But when i try to do the same with the function, it doesn't modify a list:
CL-USER> (defun push-200 (my-list)
(push 200 my-list))
PUSH-200
CL-USER> (push-200 xx)
(200 100 1 2 3)
CL-USER> xx
(100 1 2 3)
so i tried to compare argument and my list like this:
CL-USER> (defun push-200 (my-list)
(format t "~a" (eq my-list xx))
(push 200 my-list))
WARNING: redefining COMMON-LISP-USER::PUSH-200 in DEFUN
PUSH-200
CL-USER> (push-200 xx)
T
(200 100 1 2 3)
CL-USER> xx
(100 1 2 3)
it says the objects are identical.
So the question is: what was the thing I've overlooked here?
(defun push-200 (my-list)
(push 200 my-list))
This modifies the variable my-list
. The variable now points to a new cons.
It is basically: (setq my-list (cons 200 my-list)
.
(push-200 xx)
Common Lisp evaluates xx
to a value and passes the list to push-200
. xx
is not passed, but the value of it. So, the Lisp system can't modify xx
to point to a different cons, since it is not passed.
This is (essentially) the same problem highlighted by the following:
(let ((a (list 2 3 4 5)))
(let ((b a))
(push 1 a)
(list (eql a b)
(eql (cdr a) b))))
This form should return (NIL T)
when run.
What Rainer Joswig writes (and Vatine demonstrates). Just replace (push <A> <B>)
with (setq <B> (cons <A> <B>))
wherever you see it, because push
is a macro:
"push item place => new-place-value"
And,
"new-place-value [is] a list (the new value of place)"
Notice CLHS didn't say "(the updated structure referred to by place)".
So, the value of place changes. And that place, in your case, is the local variable my-list
. It was eq
to xx
before the (setq ...)
, but obviously not, after it. To actually alter a list structure you can use rplaca
and ⁄ or rplacd
.
We can think about argument passing in Common Lisp mostly as if what was passed is a pointer to a value; the pointer itself is passed by value, i.e. copied. So xx
"points" at some list; and initially my-list
"points" at the same memory location. That's why the initial eq
test succeeds.