Common lisp 公共Lisp共享结构混乱

Common lisp 公共Lisp共享结构混乱,common-lisp,practical-common-lisp,Common Lisp,Practical Common Lisp,我正在读《实用公共Lisp》,在第22章284页的脚注5中,我看到了一个让我困惑的代码片段 我知道变量list和tail有一个共同的列表结构, 但是我很困惑,既然在迭代过程中每次都会给tail分配“new”的值,为什么(setf(cdr tail)new)会影响变量列表的状态呢 (do ((list nil) (tail nil) (i 0 (1+ i))) ((> i 10) list) (let ((new (cons i nil))) (if (null list

我正在读《实用公共Lisp》,在第22章284页的脚注5中,我看到了一个让我困惑的代码片段

我知道变量list和tail有一个共同的列表结构, 但是我很困惑,既然在迭代过程中每次都会给tail分配“new”的值,为什么(setf(cdr tail)new)会影响变量列表的状态呢

(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)

不变量是
tail
始终是
列表的最后一个cons单元格

在每次迭代中,将创建一个新的cons单元格,其中包含新值
car
。第一次通过时,
list
被设置到此cons单元格,并且
tail
也被设置。在所有后续迭代中,通过将最后一个单元格的
cdr
设置为新的cons单元格,然后将
tail
设置为新单元格,以重新创建不变量,从而追加新的cons单元格

第一次迭代后:

 [ 0 | nil ]
   ^- list
   ^- tail
第二次之后:

 [ 0 | -]--->[ 1 | nil ]
   ^- list     ^- tail
第三:

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

示例中的
do
表单保留一个指向尾部cons的指针,以使将元素追加到列表末尾成为一种廉价的操作。否则,您需要一直遍历列表以查找最后一个cons,例如使用
append
ncoc
。另一种方法是在头部和末端收集新元素,以反转结果列表

人们可能期望
循环
宏通过将更高级别的循环表达式转换为高效代码来实现一些高效的功能

宏形式

(loop for i upto 10 collect i)
可能会扩展到与您的
do
示例类似的内部工作方式。
loop
的优点是不需要实现跟踪尾部的逻辑,因为这是宏应该为您做的