Common lisp push from function
Solution 1:
If you write functions in Lisp it is preferable to think 'functionally'. A function takes values and returns values. A typical rule would be to avoid side effects. So your function should return a result value, not 'modify' a variable value.
Instead of:
(defparameter *result* '())
(defun foo (a)
(push a *result*))
use:
(defparameter *result* '())
(defun foo (a result)
(push a result)
result)
(setf *result* (foo a *result*))
Note also that aggregate
does not need the progn
.
Slightly advanced (don't do that):
If you have a global list:
(defparameter *foo* '())
You can't push onto it, as we have seen, like this:
(defun foo (l)
(push 1 l))
If you call foo
the variable *foo*
is unchanged. Reason: Lisp does not pass a variable reference, it passes the value of the variable.
But how can we pass a reference? Well, pass a reference: a cons cell would do it (or a structure, a vector, a CLOS object, ...):
CL-USER 38 > (defparameter *foo* (list '()))
*FOO*
CL-USER 39 > (defun foo (ref)
(push 1 (first ref)))
FOO
CL-USER 40 > (foo *foo*)
(1)
CL-USER 41 > (foo *foo*)
(1 1)
Now, if we look at *foo*
, it is changed. But we haven't really changed the variable. We have changed the first entry of the list.
CL-USER 42 > *foo*
((1 1))
But, don't do it. Program in a functional style.
Solution 2:
You cannot modify the contents of a variable with a function that only takes the variable's value.
Take the following simple example:
(defun futile-push (thing list)
(push thing list))
(let ((foo (list 1)))
(futile-push 2 foo))
What happens?
-
Foo
is evaluated to the list it points to. -
2
evaluates to 2. - These two arguments are passed to the function.
Inside the function invocation:
-
Thing
is now bound to 2. -
List
is now bound to the list(1)
.
Note that the list does not know that it is also referenced by the variable
foo
outside the function.
foo
|
v
---------
list -> | 1 |NIL|
---------
-
Push
modifies the variablelist
in such a way that it is now bound to the list(2 1)
.
Note that this does not affect foo
outside. Foo
still points to
the same thing as before.
foo
|
v
--------- ---------
list -> | 2 | ----> | 1 |NIL|
--------- ---------
Futile-push
returns the return value of thepush
form, which happens to be the new value oflist
.-
That return value is never used or bound, so it vanishes.
foo | v --------- | 1 |NIL| ---------
The most straightforward way to do what you want is to return the new value and then set the variable outside:
(let ((foo (list 1)))
(setf foo (not-so-futile-push 2 foo)))
If you need to do that at more than one place, it might be worthwhile
to write a macro for that which expands to the setf
form. Note that
push
is itself a macro for exactly these reasons.