我想知道在Common Lisp中执行类似操作的最有效(algrotihmically)和可读方式是什么:
(setq result-list (append result-list small-list))
也就是说,(append-destructively result-list small-list)
之类的内容会确保result-list
包含连接列表吗?
我这样做是为了提高可读性,但在(功能)编程实践方面它是一个好主意吗?
答案 0 :(得分:4)
如果你必须在同一个清单上一遍又一遍地做,
tail-wagging
可能是最好的解决方案(我从Edi Weitz的书中学到的):
(defparameter *l* (list 'a 'b 'c 'd 'e 'f))
给*l*
一个名字的最后一个单元格
(defparameter *tail* (last *l*))
现在,(cdr *tail*)
是列表的最后一个元素:'()
。
假设您要将(list 'g 'h 'i)
添加到其结尾。
将(setf
)分配给(cdr *tail*)
,这是列表cons
的最后一个'()
- 单元格:*l*
,
并将(setf
)新的最后一个元素重新分配给 tail 。
(setf (cdr *tail*) (list 'g 'h 'i)
*tail* (last *tail*))
现在,*l*
被变异以包含第二个列表。
和*tail*
命名此新列表的最后一个cons
单元格。
*l*
;; (a b c d e f g h i)
*tail*
;; (i)
下一次,当*tail*
必须通过列表进行扩展时,您不必再次遍历所有列表,但只能分配给cdr
*tail*
待附加列表然后修改原始列表。
tailp
时,我偶然发现了tail-wagging
答案 1 :(得分:3)
destructive append
is called nconc
。
但请注意,仍需要
(setf result-list (nconc result-list small-list))
因为如果result-list
是nil
,那么(nconc result-list small-list)
是small-list
而result-list
未经修改。
需要注意的另一个问题是nconc
和append
都是
{em>线性 result-list
长度,所以这是不一个非常好的方法
存储了大量数据。
答案 2 :(得分:2)
看看你的问题,我会猜测你想要什么,并提出一些提示,如果你想反复扩展一个列表,但又不想一次又一次地扫描它。
如果您处于循环中,可以使用collect
,append
或nconc
(defun nconcat (lists)
(loop for list in lists nconc list))
如果您想在更一般的设置中执行此操作,我们的想法是跟踪列表的结尾,以便追加只需要扫描短列表。例如:
(defun nconcat (lists)
(let* ((result-ref (list nil))
(end result-ref))
(mapc
(lambda (list)
(setf (cdr end) list
end (last end)))
lists)
(cdr result-ref)))