WanderingPhd
WanderingPhd

Reputation: 189

Setting global variable reverts to last value

I'm getting some strange behavior when I try to set a global parameter within a method.

(defparameter *global-var-1* nil)

(defun method1 ()
   (setf *global-var-1* '())
   (format t "~a~%" *global-var-1*)

   ...
   (loop
      ...

      (setf *global-var-1* '(a))
      (format t "~a~%" *global-var-1*)

      (nconc *global-var-1* (list '(b c))))

In the above code, when I call method1, the first format statement always prints nil as expected. The second format statement prints (A) the first time method1 is called, but the second time it prints (A (B C)). The third time (A (B C) (B C)) and so on. Instead of setting *global-var-1* to (A), setf seems to be setting it to the previous known value. What am I doing wrong? BTW, I was setting *global-var-1 to (A) because nconc won't work with an empty list. I remove (A) later on before exiting method1.

Upvotes: 2

Views: 135

Answers (1)

danlei
danlei

Reputation: 14291

Of course nconc works with empty lists, but you must always assign its return value, like this:

CL-USER> (defparameter *x* nil)
*X*
CL-USER> (setq *x* (nconc *x* (list 'a)))
(A)
CL-USER> *x*
(A)

Then, your problem is solved by not using literal lists:

CL-USER> (defparameter *x* nil)
*X*
CL-USER> (defun foo ()
           (setf *x* nil)
           (dotimes (n 3)
             (progn (setf *x* (list 'a))
                    (format t "~a~%" *x*)
                    (setf *x* (nconc *x* (list (list 'b 'c)))))))
FOO
CL-USER> (foo)
(A)
(A)
(A)

You should always be careful when using destructive operations, and be aware of the implications of using them with literal expressions. In your code, nconc destructively modified the cdr of your '(a) list literal, which results in the behavior you observed. (You could always use append to get around this, if you're not optimizing.)

You might also be interested in my other answer about this topic.

Upvotes: 6

Related Questions