In this chapter we'll meet the most useful sequence functions, and see how to use them. We'll also reprise earlier admonitions about proper use of destructive functions.
CONCATENATE
always creates a new sequence from (of course)
the concatenation of zero or more argument sequences. You must specify the
type of the result, and the argument types must be proper subtypes of the
sequence type.
? (concatenate 'list) ; no argument sequences NIL ? (concatenate 'vector) ; no argument sequences #() ? (concatenate 'list '(1 2 3) (4 5)) (1 2 3 4 5) ? (concatenate 'vector #(1 2 3) #(4 5)) #(1 2 3 4 5) ? (concatenate 'list #(1 2 4) '(4 5)) (1 2 3 4 5) ? (concatenate 'vector '(1 2 3) #(4 5)) #(1 2 3 4 5) ? (concatenate 'list "hello") ; string is a subtype of sequence (#\h #\e #\l #\l #\o)
If you need to pick out one element (or a range of elements) from a
sequence, you can use ELT
(to pick out one element) or
SUBSEQ
(to pick out a range of elements). But don't use these
unless you're really sure you can't narrow down the sequence type to a
vector or list; there are more specific (hence more efficient) accessors
for the less general types.
SUBSEQ
makes a copy of a specified portion of a sequence.
COPY-SEQ
is closely related to SUBSEQ
, except
that it copies all of the elements of a sequence.
? (elt '(1 2 3 4 5) 1) ; zero-based indexing 2 ? (subseq '(1 2 3 4 5) 2) ; 3rd element through end (3 4 5) ? (let ((l '(1 2 3 4 5))) (subseq l 2 (length l))) ; same effect as previous ? (subseq '(1 2 3 4 5) 0 3) ; element at ending index is not copied (1 2 3) ? (subseq #(#\a #\b #\c #\d #\e) 2 4) #(#\c #\d) ? (copy-seq '(a b c)) (A B C)
REVERSE
makes a copy of a sequence, with the order of
elements reversed. NREVERSE
is the destructive counterpart
of REVERSE
; it is more efficient, but it modifies its
input argument.
REVERSE
is commonly used in code similar to the following.
(defun collect-even-numbers (number-list) (let ((result ())) (dolist (number number-list) (when (evenp number) (push number result))) (nreverse result)))
The DOLIST
and PUSH
collect even
numbers on the result list, but they are in the reverse order of
their original positions on the input list. The final
NREVERSE
puts them back into their original order. This
is a safe use of the destructive function NREVERSE
because the RESULT
variable can not be shared; it is
forgotten as soon as control leaves the LET
form.
There's not much to say about LENGTH
. Just remember
that for lists, LENGTH
counts only the elements of the
top-level list, and not those of any nested lists.
? (length '((1 2 3) (4 5) (6) 7 () 8 9)) 7
If you find your program filters a sequence only to get the length
of the result, use COUNT
(and related functions
COUNT-IF
and COUNT-IF-NOT
) instead.
? (count 3 '(1 3 3 4 2 5 9 8 3 1 9)) ; count occurrences 3 ? (count-if #'oddp '(1 3 3 4 2 5 9 8 3 1 9)) ; count matches to predicate 8 ? (count-if-not #'evenp '(1 3 3 4 2 5 9 8 3 1 9)) ; count mismatches using predicate 8
These functions accept keyword arguments:
Keyword Value Default ------- ----- ------- :START starting index (inclusive) 0 :END ending index (exclusive) NIL :FROM-END non-NIL to work backwards from end element NIL :KEY function to select match data from element NIL
A NIL
value for the :END
keyword designates
a position just past the end of the sequence; since this is an exclusive
limit, the last element will be processed. (If you specified the index
of the last element, the last element would not be processed.)
The :FROM-END
keyword is useful in the case that the
test function has side-effects, and the order of the side-effects is
important.
When the :KEY
argument is not NIL
, it should
be a function of one argument that extracts data from the sequence element.
For example:
? (count 3 '((1 2 3) (2 3 1) (3 1 2) (2 1 3) (1 3 2) (3 2 1)) :key #'second) 2
COUNT
accepts the additional keyword arguments
:TEST
and :TEST-NOT
. These give you a compact
way to write a test that involves a second value. Compare the following
equivalent forms:
; Using COUNT-IF and LAMBDA (count-if #'(lambda (n) (< 3 n)) '(1 2 3 4 5 6 7)) ; Using COUNT and :TEST (count 3 '(1 2 3 4 5 6 7) :test #'<)
The keyword arguments for comparison predicates also let you
define the precise meaning of equality. The default predicate is
EQL
, which is true for identical numbers and symbols.
See Chapter 17 for more information on
comparison predicates.
REMOVE
removes all occurrences of a specified
element from a sequence.
? (remove 7 '(1 2 3 a b c t nil 7 0 7 7)) (1 2 3 A B C T NIL 0)
Keyword arguments are handled in the same way as for
COUNT
. REMOVE-IF
and
REMOVE-IF-NOT
are also available; their keyword
arguments are handled in the same way as for COUNT-IF
and COUNT-IF-NOT
.
A :COUNT
keyword argument lets you limit the number
of matching elements to remove.
SUBSTITUTE
changes all occurrences of a specified
element in a sequence to another value.
? (substitute '(q) 7 '(1 2 3 a b c t nil 7 0 7 7)) (1 2 3 A B C T NIL (Q) 0 (Q) (Q))
Keyword arguments are handled in the same way as for
COUNT
. SUBSTITUTE-IF
and
SUBSTITUTE-IF-NOT
are also available; their keyword
arguments are handled in the same way as for COUNT-IF
and COUNT-IF-NOT
.
A :COUNT
keyword argument lets you limit the number
of matching elements to substitute.
REMOVE-DUPLICATES
returns a copy of a sequence, modified
so that every element is different.
? (remove-duplicates '(1 2 3 a b c (1 2 3) f c g c h b i a j b a k a)) (1 2 3 (1 2 3) F G C H I J B K A)
The last copy of each identical element is retained in the result, unless
you specify the keyword argument :FROM-END T
, which causes the
first copy of each identical element to be retained.
REMOVE-DUPLICATES
also accepts the same keyword
arguments as COUNT
. The :TEST
and
:TEST-NOT
keyword arguments let you specify the
comparison predicate used to determine whether elements are
identical. The default predicate is EQL
, which is true
for identical numbers and symbols. See Chapter 17 for more information on
comparison predicates.
Many of the functions in the preceeding section have destructive counterparts. The result of the destructive functions is identical, but the input sequence may be destructively modified.
Nondestructive Destructive -------------- ----------- REMOVE DELETE REMOVE-IF DELETE-IF REMOVE-IF-NOT DELETE-IF-NOT SUBSTITUTE NSUBSTITUTE SUBSTITUTE-IF NSUBSTITUTE-IF SUBSTITUTE-IF-NOT NSUBSTITUTE-IF-NOT REMOVE-DUPLICATES DELETE-DUPLICATES
Remember that you must not depend upon the modification of the input sequences. The only result guaranteed to be correct is the return value of the function.
FILL
destructively modifies a sequence, replacing
every element with a new value. It accepts keyword arguments for
:START
and :END
positions; these have the
same meaning as described earlier in this chapter. The modified
sequence is returned as the value of FILL
.
? (fill (list 1 1 2 3 5 8) 7) (7 7 7 7 7 7) ? (fill (list 1 1 2 3 5 8) '(a b)) ((A B) (A B) (A B) (A B) (A B) (A B)) ? (fill (list 1 1 2 3 5 8) 7 :start 2 :end 4) (1 1 7 7 5 8)
REPLACE
copies elements from one sequence into another,
destructively modifying the target sequence. You can specify the range
of elements to use in both sequences; the shorter of the two ranges
determines the number of elements that is actually copied.
? (let ((a (list 1 2 3 4 5 6 7)) (b (list 9 8 7 6 5 4 3))) (replace a b)) (9 8 7 6 5 4 3) ? (let ((a (list 1 2 3 4 5 6 7)) (b (list 9 8 7 6 5 4 3))) (replace a b :start1 2)) (1 2 9 8 7 6 5) ? (let ((a (list 1 2 3 4 5 6 7)) (b (list 9 8 7 6 5 4 3))) (replace a b :start1 2 :end1 5)) (1 2 9 8 7 6 7) ? (let ((a (list 1 2 3 4 5 6 7)) (b (list 9 8 7 6 5 4 3))) (replace a b :start1 2 :end1 5 :start2 3)) (1 2 6 5 4 6 7) ? (let ((a (list 1 2 3 4 5 6 7)) (b (list 9 8 7 6 5 4 3))) (replace a b :start1 2 :end1 5 :start2 3 :end2 4)) (1 2 6 4 5 6 7)
POSITION
searches a sequence for a matching element, and
returns the index of the first match or NIL
if no matching
element is in the sequence.
? (position #\a "This is all about you, isn't it?") 8 ? (position #\! "This is all about you, isn't it?") NIL
POSITION
accepts the same keyword arguments as
COUNT
(described earlier in this chapter) and has (the
by now familar) variants POSITION-IF
and
POSITION-IF-NOT
.
FIND
is similar to POSITION
except that
the matching element -- rather than its index in the sequence -- is
returned if there is a match. As with POSITION
, you'll
find the usual keyword arguments (:FROM-END
,
:START
, :END
, :KEY
-- and for
the "base" function, :TEST
and :TEST-NOT
)
and function variants (i.e. FIND-IF
and
FIND-IF-NOT
).
? (find #\a "This is all about you, isn't it?") #\a ? (find #\! "This is all about you, isn't it?") NIL
SEARCH
returns the starting position of one sequence
within another sequence, or NIL
if no match is found.
? (search "ab" "This is all about you, isn't it?") 12 ? (search "not so" "This is all about you, isn't it?") NIL
SEARCH
accepts :FROM-END
, :KEY
,
:TEST
and :TEST-NOT
keyword arguments with the
usual interpretations. You can specify a range in the substring (the first
argument) using :START1
and :END1
keywords, and
in the target string using the :START2
and :END2
keywords.
MISMATCH
is the functional complement to
SEARCH
-- it returns the first position at which the
substring fails to match a portion of the target string.
? (mismatch "banana" "bananananono") 6 ? (mismatch "." "...hello") 1 ? (mismatch "............." "...hello") 3
SORT
destructively sorts a sequence; the order is
determined by a predicate which you supply.
? (sort (list 9 3 5 4 8 7 1 2 0 6) #'>) (9 8 7 6 5 4 3 2 1 0) ? (sort (list 9 3 5 4 8 7 1 2 0 6) #'<) (0 1 2 3 4 5 6 7 8 9)
The input sequence is destructively modified -- you must use the function result.
STABLE-SORT
preserves the original order of
identical elements; SORT
may not.
You can sort structured elements (e.g. lists, structures) by using the
:KEY
keyword argment to specify a key extraction function.
MERGE
combines two input sequences into a
single result. Elements are interleaved according to the predicate.
Either input sequence may be destructively modified. You must
designate the type of the result.
? (merge 'vector (list 1 3 5 9 8) (vector 2 6 4 7 0) #'>) #(2 6 4 7 1 3 5 9 8 0) ? (merge 'list (list 1 3 5 9 8) (vector 2 6 4 7 0) #'<) (1 2 3 5 6 4 7 0 9 8) ? (merge 'vector (list 1 3 5 8 9) (vector 0 2 4 6 7) #'>) #(1 3 5 8 9 0 2 4 6 7) ? (merge 'list (list 1 3 5 8 9) (vector 0 2 4 6 7) #'<) (0 1 2 3 4 5 6 7 8 9)
Note that -- in the general case -- MERGE
does
not sort the catenation of its arguments. The predicate is
used to select from one or the other of the input sequences; input
from the selected sequence continues until the sense of the
predicate changes. Look at the examples until you understand this.
MERGE
accepts a :KEY
keyword argument
having the conventional meaning.