0

假设我有名称为“数字”的列表 (3 1 4 5 2)。我正在寻找一个命令,它将列表从索引 0 反转到任意索引,即(反转数字 2),它将给新列表作为(4 1 3 5 2)。

我试过谷歌搜索,但找不到合适的函数,而且我太新手了,无法在这个阶段自己编写函数。

谢谢你。

4

2 回答 2

6

基于库函数的简单 CL 版本:

(defun reverse-first-n (list n)
  (nreconc (subseq list 0 n) (nthcdr n list)))
  1. 这是内存优化的,即它不会进行不必要的分配:
    • 不需要复制尾部,因此nthcdr而不是subseq
    • revappend复制第一个参数,无论如何它都是一个新列表,所以nreconc更经济。
  2. 这个版本是速度次优的,因为它遍历listnth 位置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 库为您节省了多少时间和精力。

于 2013-08-04T17:12:24.253 回答
2

你用的是哪种 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!nreconccall-with-valuesmultiple-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) 提供输入列表的一次遍历。)

于 2013-08-04T15:32:13.807 回答