0

I have the following Common Lisp code and I'm confused about how the counter variable is bound.

(defun count-bases (strand)
  (let ((counter (list '(a . 0) '(c . 0) '(g . 0) '(t . 0))))
    (inspect counter)
    (labels ((inc-counter (base)
               (incf (cdr (assoc base counter)))))
      (dolist (element strand (mapcar #'(lambda (pair) (list (car pair) (cdr pair))) counter))
        (cond ((symbolp element) (inc-counter element))
              (t (inc-counter (first element))
                 (inc-counter (second element))))))))

I would expect that every time I call this function, the value '((a . 0) (c . 0) (g . 0) (t . 0)) is bound to the counter variable, and the counter variable goes out of scope when the function completes. However, the second time I call the function (shown below), the counter variable retains its value from the first function call.

CL-USER> (count-bases '(a t t a g c a c))

The object is a proper list of length 4.
0. 0: (A . 0)
1. 1: (C . 0)
2. 2: (G . 0)
3. 3: (T . 0)
> E

((A 3) (C 2) (G 1) (T 2))
CL-USER> (count-bases '(a t t a g c a c))

The object is a proper list of length 4.
0. 0: (A . 3)
1. 1: (C . 2)
2. 2: (G . 1)
3. 3: (T . 2)
> E

((A 6) (C 4) (G 2) (T 4))
CL-USER> 

Can somebody please explain what's happening here and what I have to do to bind the variable as expected each time I call the function? Thank you very much.

jay
  • 1,524
  • 4
  • 25
  • 45

1 Answers1

4

This is an often asked question.

You are modifying literal constant data in the source code. The cons cells in the LIST call are all literal constants. You have quoted them.

Use copy-tree to create a fresh list.

(let ((counter (copy-tree '((a . 0) (c . 0) (g . 0) (t . 0)))))
  ...)

Alternatively call cons instead of quoting the cons cells.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346