假设我有名称为“数字”的列表 (3 1 4 5 2)。我正在寻找一个命令,它将列表从索引 0 反转到任意索引,即(反转数字 2),它将给新列表作为(4 1 3 5 2)。
我试过谷歌搜索,但找不到合适的函数,而且我太新手了,无法在这个阶段自己编写函数。
谢谢你。
基于库函数的简单 CL 版本:
(defun reverse-first-n (list n)
(nreconc (subseq list 0 n) (nthcdr n list)))
list
到n
th 位置3次——一次 in subseq
,一次 in nthcdr
,然后一次 in nreconc
。这是最佳版本:
(defun reverse-first-n (list n)
(if (or (= n 0) (= n 1))
list
(do* ((tail (list (pop list)))
(head tail (cons (pop list) head))
(count (1- n) (1- count)))
((zerop count)
(setf (cdr tail) list)
head))))
请注意,这是代码中的性能瓶颈的可能性很小。我提供第二个版本的主要目的是展示广泛且精心设计的 CL 库为您节省了多少时间和精力。
你用的是哪种 Lisp 方言?这是一个方案解决方案(使用SRFI 1):
(require srfi/1) ; assuming you're using Racket
(define (reverse-first-n lst n)
(call-with-values (lambda ()
(split-at lst n))
append-reverse!))
我使该功能真正“反转前n个元素”,就像您的标题所说的那样,与您的问题描述不同。例如:
> (reverse-first-n '(3 1 4 5 2) 2)
'(1 3 4 5 2)
> (reverse-first-n '(3 1 4 5 2) 3)
'(4 1 3 5 2)
根据 OP 的要求,这是一个 Common Lisp 版本。sds 已经发布了一个相当不错的版本,所以我正在编写的版本是我的 Scheme 解决方案的更直接端口(append-reverse!
⇒ nreconc
;call-with-values
⇒ multiple-value-call
;我正在将 SRFI 1 移植split-at
到 CL):
(defun split-at (list n)
(if (zerop n)
(values '() list)
(multiple-value-bind (prefix suffix)
(split-at (cdr list) (1- n))
(values (cons (car list) prefix) suffix))))
(defun reverse-first-n (list n)
(multiple-value-call #'nreconc (split-at list n)))
(为什么split-at
?它的目的是为take
( subseq
) 和drop
( nthcdr
) 提供输入列表的一次遍历。)