我想编写一个反转列表元素的函数,但它应该发生在适当的位置(也就是说,不要创建一个新的反转列表)。
就像是:
>> (setq l ' (a b c d))
((a b c d)
>> (rev l)
(d c b a)
>> l
(d c b a)
我应该遵循哪些标志来实现这一目标?
我想编写一个反转列表元素的函数,但它应该发生在适当的位置(也就是说,不要创建一个新的反转列表)。
就像是:
>> (setq l ' (a b c d))
((a b c d)
>> (rev l)
(d c b a)
>> l
(d c b a)
我应该遵循哪些标志来实现这一目标?
Have a look at nreverse
which will modify the list in place (see HyperSpec).
As per the comments, do note the comments that @Barmar made and this bit from the spec:
For nreverse, sequence might be destroyed and re-used to produce the result. The result might or might not be identical to sequence. Specifically, when sequence is a list, nreverse is permitted to setf any part, car or cdr, of any cons that is part of the list structure of sequence.
实现这一点并不难(忽略故障情况)。密钥(setf cdr)
用于重用给定的 cons 单元格,而不是丢失对先前 cdr 的引用。
(defun nreverse2 (list)
(recurse reving ((list list) (rslt '()))
(if (not (consp list))
rslt
(let ((rest (cdr list)))
(setf (cdr list) rslt)
(reving rest list)))))
(defmacro recurse (name args &rest body)
`(labels ((,name ,(mapcar #'car args) ,@body))
(,name ,@(mapcar #'cadr args))))
[编辑] 如评论中所述,要真正就地执行此操作(并且不考虑 consing):
(defun reverse-in-place (l)
(let ((result l))
(recurse reving ((l l) (r (reverse l))
(cond ((not (consp l)) result)
(else (setf (car l) (car r))
(reving (cdr l) (cdr r)))))))
> (defvar l '(1 2 3))
> (reverse-in-place l))
(3 2 1)
> l
(3 2 1)