3.11 序列 (Sequences)

另一种考虑一个列表的方式是想成一系列有特定顺序的对象。在 Common Lisp 里,序列( sequences )包括了列表与向量 (vectors)。本节介绍了一些可以运用在列表上的序列函数。更深入的序列操作在 4.4 节讨论。

函数 length 返回序列中元素的数目。

  1. > (length '(a b c))
  2. 3

我们在 24 页 (译注:2.13节 our-length )写过这种函数的一个版本(仅可用于列表)。

要复制序列的一部分,我们使用 subseq 。第二个(需要的)参数是第一个开始引用进来的元素位置,第三个(选择性)参数是第一个不引用进来的元素位置。

  1. > (subseq '(a b c d) 1 2)
  2. (B)
  3. >(subseq '(a b c d) 1)
  4. (B C D)

如果省略了第三个参数,子序列会从第二个参数给定的位置引用到序列尾端。

函数 reverse 返回与其参数相同元素的一个序列,但顺序颠倒。

  1. > (reverse '(a b c))
  2. (C B A)

一个回文 (palindrome) 是一个正读反读都一样的序列 —— 举例来说, (abba) 。如果一个回文有偶数个元素,那么后半段会是前半段的镜射 (mirror)。使用 lengthsubseq 以及 reverse ,我们可以定义一个函数

  1. (defun mirror? (s)
  2. (let ((len (length s)))
  3. (and (evenp len)
  4. (let ((mid (/ len 2)))
  5. (equal (subseq s 0 mid)
  6. (reverse (subseq s mid)))))))

来检测是否是回文:

  1. > (mirror? '(a b b a))
  2. T

Common Lisp 有一个内置的排序函数叫做 sort 。它接受一个序列及一个比较两个参数的函数,返回一个有同样元素的序列,根据比较函数来排序:

  1. > (sort '(0 2 1 3 8) #'>)
  2. (8 3 2 1 0)

你要小心使用 sort ,因为它是破坏性的(destructive)。考虑到效率的因素, sort 被允许修改传入的序列。所以如果你不想你本来的序列被改动,传入一个副本。

使用 sortnth ,我们可以写一个函数,接受一个整数 n ,返回列表中第 n 大的元素:

  1. (defun nthmost (n lst)
  2. (nth (- n 1)
  3. (sort (copy-list lst) #'>)))

我们把整数减一因为 nth 是零索引的,但如果 nthmost 是这样的话,会变得很不直观。

  1. (nthmost 2 '(0 2 1 3 8))

多努力一点,我们可以写出这个函数的一个更有效率的版本。

函数 everysome 接受一个判断式及一个或多个序列。当我们仅输入一个序列时,它们测试序列元素是否满足判断式:

  1. > (every #'oddp '(1 3 5))
  2. T
  3. > (some #'evenp '(1 2 3))
  4. T

如果它们输入多于一个序列时,判断式必须接受与序列一样多的元素作为参数,而参数从所有序列中一次提取一个:

  1. > (every #'> '(1 3 5) '(0 2 4))
  2. T

如果序列有不同的长度,最短的那个序列,决定需要测试的次数。