Lisp 更改列表的第n个元素
我想更改列表的第n个元素并返回一个新列表 我想到了三个相当不雅观的解决方案:Lisp 更改列表的第n个元素,lisp,common-lisp,Lisp,Common Lisp,我想更改列表的第n个元素并返回一个新列表 我想到了三个相当不雅观的解决方案: (defun set-nth1 (list n value) (let ((list2 (copy-seq list))) (setf (elt list2 n) value) list2)) (defun set-nth2 (list n value) (concatenate 'list (subseq list 0 n) (list value) (subseq list (1+ n))
(defun set-nth1 (list n value)
(let ((list2 (copy-seq list)))
(setf (elt list2 n) value)
list2))
(defun set-nth2 (list n value)
(concatenate 'list (subseq list 0 n) (list value) (subseq list (1+ n))))
(defun set-nth3 (list n value)
(substitute value nil list
:test #'(lambda (a b) (declare (ignore a b)) t)
:start n
:count 1))
做这件事最好的方法是什么?怎么样
(defun set-nth4 (list n val)
(loop for i from 0 for j in list collect (if (= i n) val j)))
也许我们应该注意到与替换的相似性,并遵循其惯例:
(defun substitute-nth (val n list)
(loop for i from 0 for j in list collect (if (= i n) val j)))
顺便说一句,关于set-nth3
,有一个函数,正好适用于这种情况:
(defun set-nth3 (list n value)
(substitute value nil list :test (constantly t) :start n :count 1))
编辑:
另一种可能性:
(defun set-nth5 (list n value)
(fill (copy-seq list) value :start n :end (1+ n)))
这取决于你所说的“优雅”是什么意思,但是
(defun set-nth (list n val)
(if (> n 0)
(cons (car list)
(set-nth (cdr list) (1- n) val))
(cons val (cdr list))))
如果您在容易理解递归定义方面存在问题,那么对您来说,N-2的一个细微变化(如Terje Norderhaug所建议的)应该更“不言而喻”:
我能看到的这个版本唯一的效率缺陷是遍历第n个元素要三次,而不是递归版本中的一次(但不是尾部递归)。这样如何:
(defun set-nth (list n value)
(loop
for cell on list
for i from 0
when (< i n) collect (car cell)
else collect value
and nconc (rest cell)
and do (loop-finish)
))
(定义设置n(列表n值)
(环路
对于列表上的单元格
我从0开始
当(
从负面来看,它更像是Algol而不是Lisp。但从好的方面来说:
- 它只遍历输入列表的前导部分一次
- 它根本不遍历输入列表的尾部
- 构建输出列表时无需再次遍历它
- 结果与原始列表共享相同的尾随cons单元格(如果不需要,请将
更改为ncoc
)append
nthcdr
重用原始列表的尾部。此外,由于subseq
总是创建一个新序列,您可以使用破坏性的ncoc
而不是串联
,以减少考虑和提高性能。就命名而言,我希望任何名为SET-。。。是破坏性的,而不是复制。我可能会把它称为COPY-WITH-SHADOWED-NTH或类似的。此外,由于需要进行尾部共享,因此很难找到正确的、描述性的、简短的名称。您可以在第三个示例中使用(经常使用t)
,我的意思是,通过查看代码,代码的目的应该是不言而喻的。到目前为止,只有set-nth1通过了此测试。而且,它应该是高效的,因此没有不必要的考虑。set-nth-2bis
应该是不言而喻的,并且尽量减少考虑。使用cons
而不是(cons-val(nthcdr(1+n)list))
中的list可能会稍微提高可读性,但这是一个品味问题。@Terje:从(list…)改为(cons…)谢谢你,也谢谢你告诉我关于“持续”的事情——我经常需要它!嗯……所以,可能LISP在性能上不如第n个元素好,我们需要遍历n个值,在系统语言中,我们只需向指针添加(n*sizeof(element))
。这是任何大小的元素都可以保存的代价。必须“通过n个值”是选择链表作为数据结构的结果,而不是因为选择编程语言。如果随机访问很普遍,那么选择向量或数组,CommonLisp当然提供了这些。(如果你正在阅读垃圾类的材料,比如“LISP中唯一的数据结构是list”,扔掉它。它已经过时了。)
(defun set-nth (list n value)
(loop
for cell on list
for i from 0
when (< i n) collect (car cell)
else collect value
and nconc (rest cell)
and do (loop-finish)
))