common-lisppractical-common-lisp

Common Lisp shared structure confusion


I am reading the book Practical Common Lisp, and in the footnote5 of chapter22, page284, I saw a code snippet that made me confused.

I know that the variable list and tail have a list structure in common, but I am confusing that since tail will be assigned the value of 'new' each time during the iteration, why (setf (cdr tail) new) would affect the state of the variable list?

(do ((list nil) (tail nil) (i 0 (1+ i)))
    ((> i 10) list)
  (let ((new (cons i nil)))
    (if (null list)
        (setf list new)
        (setf (cdr tail) new))
    (setf tail new)))

;;; => (0 1 2 3 4 5 6 7 8 9 10)

Solution

  • The invariant is that tail is always the last cons cell of list.

    On each iteration, a new cons cell is created holding the new value as car. The first time through, list is set to this cons cell, and tail too. In all subsequent iterations, the new cons cell is appended by setting the cdr of the last cell to it, then setting tail to the new cell, to recreate the invariant.

    After first iteration:

     [ 0 | nil ]
       ^- list
       ^- tail
    

    After second:

     [ 0 | -]--->[ 1 | nil ]
       ^- list     ^- tail
    

    Third:

     [ 0 | -]--->[ 1 | -]--->[ 2 | nil ]
       ^- list                 ^- tail