我们已经看过四种用来取出序列元素的函数: 给列表使用的 nth , 给向量使用的 arefsvref ,以及给字符串使用的 char 。 Common Lisp 也提供了通用的 elt ,对任何种类的序列都有效:

    1. > (elt '(a b c) 1)
    2. B

    针对特定类型的序列,特定的存取函数会比较快,所以使用 elt 是没有意义的,除非在代码当中,有需要支持通用序列的地方。

    使用 elt ,我们可以写一个针对向量来说更有效率的 mirror? 版本:

    1. (defun mirror? (s)
    2. (let ((len (length s)))
    3. (and (evenp len)
    4. (do ((forward 0 (+ forward 1))
    5. (back (- len 1) (- back 1)))
    6. ((or (> forward back)
    7. (not (eql (elt s forward)
    8. (elt s back))))
    9. (> forward back))))))

    这个版本也可用在列表,但这个实现更适合给向量使用。频繁的对列表调用 elt 的代价是昂贵的,因为列表仅允许顺序存取。而向量允许随机存取,从任何元素来存取每一个元素都是廉价的。

    许多序列函数接受一个或多个,由下表所列的标准关键字参数:

    一个接受所有关键字参数的函数是 position ,返回序列中一个元素的位置,未找到元素时则返回 nil 。我们使用 position 来演示关键字参数所扮演的角色。

    1. > (position #\a "fantasia")
    2. > (position #\a "fantasia" :start 3 :end 5)
    3. 4

    第二个例子我们要找在第四个与第六个字符间,第一个 a 所出现的位置。 :start 关键字参数是第一个被考虑的元素位置,缺省是序列的第一个元素。 :end 关键字参数,如果有给的话,是第一个不被考虑的元素位置。

    如果我们给入 :from-end 关键字参数,

    1. > (position #\a "fantasia" :from-end t)
    2. 7

    :key 关键字参数是序列中每个元素在被考虑之前,应用至元素上的函数。如果我们说,

    1. > (position 'a '((c d) (a b)) :key #'car)
    2. 1

    那么我们要找的是,元素的 car 部分是符号 a 的第一个元素。

    :test 关键字参数接受需要两个实参的函数,并定义了怎样是一个成功的匹配。缺省函数为 eql 。如果你想要匹配一个列表,你也许想使用 equal 来取代:

    :test 关键字参数可以是任何接受两个实参的函数。举例来说,给定 < ,我们可以询问第一个使第一个参数比它小的元素位置:

    1. > (position 3 '(1 0 7 5) :test #'<)
    2. 2

    使用 subseqposition ,我们可以写出分开序列的函数。举例来说,这个函数

    1. (defun second-word (str)
    2. (let ((p1 (+ (position #\ str) 1)))
    3. (subseq str p1 (position #\ str :start p1))))

    返回字符串中第一个单字空格后的第二个单字:

    1. > (second-word "Form follows function")

    要找到满足谓词的元素,其中谓词接受一个实参,我们使用 position-if 。它接受一个函数与序列,并返回第一个满足此函数的元素:

    1. > (position-if #'oddp '(2 3 4 5))
    2. 1

    接受除了 :test 之外的所有关键字参数。

    1. > (find #\a "cat")
    2. #\a
    3. > (find-if #'characterp "ham")
    4. #\h

    不同于 membermember-if ,它们仅返回要寻找的对象。

    通常一个 find-if 的调用,如果解读为 find 搭配一个 :key 关键字参数的话,会显得更清楚。举例来说,表达式

    可以更好的解读为

    1. (find 'complete lst :key #'car)

    函数 remove (22 页)以及 remove-if 通常都可以用在序列。它们跟 findfind-if 是一样的关系。另一个相关的函数是 remove-duplicates ,仅保留序列中每个元素的最后一次出现。

    1. > (remove-duplicates "abracadabra")
    2. "cdbra"

    这个函数接受前表所列的所有关键字参数。

    函数 reduce 用来把序列压缩成一个值。它至少接受两个参数,一个函数与序列。函数必须是接受两个实参的函数。在最简单的情况下,一开始函数用序列前两个元素作为实参来调用,之后接续的元素作为下次调用的第二个实参,而上次返回的值作为下次调用的第一个实参。最后调用最终返回的值作为 reduce 整个函数的返回值。也就是说像是这样的表达式:

    1. (reduce #'fn '(a b c d))

    等同于

    1. (fn (fn (fn 'a 'b) 'c) 'd)

    我们可以使用 reduce 来扩充只接受两个参数的函数。举例来说,要得到三个或多个列表的交集(intersection),我们可以:

    1. (A)