3.11 序列 (Sequences)
另一种考虑一个列表的方式是想成一系列有特定顺序的对象。在 Common Lisp 里,序列( sequences )包括了列表与向量 (vectors)。本节介绍了一些可以运用在列表上的序列函数。更深入的序列操作在 4.4 节讨论。
函数 length
返回序列中元素的数目。
> (length '(a b c))
3
我们在 24 页 (译注:2.13节 our-length
)写过这种函数的一个版本(仅可用于列表)。
要复制序列的一部分,我们使用 subseq
。第二个(需要的)参数是第一个开始引用进来的元素位置,第三个(选择性)参数是第一个不引用进来的元素位置。
> (subseq '(a b c d) 1 2)
(B)
>(subseq '(a b c d) 1)
(B C D)
如果省略了第三个参数,子序列会从第二个参数给定的位置引用到序列尾端。
函数 reverse
返回与其参数相同元素的一个序列,但顺序颠倒。
> (reverse '(a b c))
(C B A)
一个回文 (palindrome) 是一个正读反读都一样的序列 —— 举例来说, (abba)
。如果一个回文有偶数个元素,那么后半段会是前半段的镜射 (mirror)。使用 length
、 subseq
以及 reverse
,我们可以定义一个函数
(defun mirror? (s)
(let ((len (length s)))
(and (evenp len)
(let ((mid (/ len 2)))
(equal (subseq s 0 mid)
(reverse (subseq s mid)))))))
来检测是否是回文:
> (mirror? '(a b b a))
T
Common Lisp 有一个内置的排序函数叫做 sort
。它接受一个序列及一个比较两个参数的函数,返回一个有同样元素的序列,根据比较函数来排序:
> (sort '(0 2 1 3 8) #'>)
(8 3 2 1 0)
你要小心使用 sort
,因为它是破坏性的(destructive)。考虑到效率的因素, sort
被允许修改传入的序列。所以如果你不想你本来的序列被改动,传入一个副本。
使用 sort
及 nth
,我们可以写一个函数,接受一个整数 n
,返回列表中第 n
大的元素:
(defun nthmost (n lst)
(nth (- n 1)
(sort (copy-list lst) #'>)))
我们把整数减一因为 nth
是零索引的,但如果 nthmost
是这样的话,会变得很不直观。
(nthmost 2 '(0 2 1 3 8))
多努力一点,我们可以写出这个函数的一个更有效率的版本。
函数 every
和 some
接受一个判断式及一个或多个序列。当我们仅输入一个序列时,它们测试序列元素是否满足判断式:
> (every #'oddp '(1 3 5))
T
> (some #'evenp '(1 2 3))
T
如果它们输入多于一个序列时,判断式必须接受与序列一样多的元素作为参数,而参数从所有序列中一次提取一个:
> (every #'> '(1 3 5) '(0 2 4))
T
如果序列有不同的长度,最短的那个序列,决定需要测试的次数。