One group of sequence functions allows you to express certain operations on sequences such as finding or filtering specific elements without writing explicit loops. Table 11-1 summarizes them.
Name | Required Arguments | Returns |
COUNT | Item and sequence | Number of times item appears in sequence |
FIND | Item and sequence | Item or NIL |
POSITION | Item and sequence | Index into sequence or NIL |
REMOVE | Item and sequence | Sequence with instances of item removed |
SUBSTITUTE | New item, item, and sequence | Sequence with instances of item replaced with new item |
Here are some simple examples of how to use these functions:
(count 1 #(1 2 1 2 3 1 2 3 4)) ==> 3
(remove 1 #(1 2 1 2 3 1 2 3 4)) ==> #(2 2 3 2 3 4)
(remove 1 '(1 2 1 2 3 1 2 3 4)) ==> (2 2 3 2 3 4)
(remove #a 'foobarbaz') ==> 'foobrbz'
(substitute 10 1 #(1 2 1 2 3 1 2 3 4)) ==> #(10 2 10 2 3 10 2 3 4)
(substitute 10 1 '(1 2 1 2 3 1 2 3 4)) ==> (10 2 10 2 3 10 2 3 4)
(substitute #x # 'foobarbaz') ==> 'fooxarxaz'
(find 1 #(1 2 1 2 3 1 2 3 4)) ==> 1
(find 10 #(1 2 1 2 3 1 2 3 4)) ==> NIL
(position 1 #(1 2 1 2 3 1 2 3 4)) ==> 0
Note how REMOVE
and SUBSTITUTE
always return a sequence of the same type as their sequence argument.
You can modify the behavior of these five functions in a variety of ways using keyword arguments. For instance, these functions, by default, look for elements in the sequence that are the same object as the item argument. You can change this in two ways: First, you can use the :test
keyword to pass a function that accepts two arguments and returns a boolean. If provided, it will be used to compare EQL
.[123] Second, with the :key
keyword you can pass a one-argument function to be called on each element of the sequence to extract a FIND
that return elements of the sequence continue to return the actual element, not just the extracted key.
(count 'foo' #('foo' 'bar' 'baz') :test #'string=) ==> 1
(find 'c #((a 10) (b 20) (c 30) (d 40)) :key #'first) ==> (C 30)
To limit the effects of these functions to a particular subsequence of the sequence argument, you can provide bounding indices with :start
and :end
arguments. Passing NIL
for :end
or omitting it is the same as specifying the length of the sequence.[124]
If a non-NIL :from-end
argument is provided, then the elements of the sequence will be examined in reverse order. By itself :from-end
can affect the results of only FIND
and POSITION
. For instance:
(find 'a #((a 10) (b 20) (a 30) (b 40)) :key #'first) ==> (A 10)
(find 'a #((a 10) (b 20) (a 30) (b 40)) :key #'first :from-end t) ==> (A 30)
However, the :from-end
argument can affect REMOVE
and SUBSTITUTE
in conjunction with another keyword parameter, :count
, that's used to specify how many elements to remove or substitute. If you specify a :count
lower than the number of matching elements, then it obviously matters which end you start from:
(remove #a 'foobarbaz' :count 1) ==> 'foobrbaz'
(remove #a 'foobarbaz' :count 1 :from-end t) ==> 'foobarbz'
And while :from-end
can't change the results of the COUNT
function, it does affect the order the elements are passed to any :test
and :key
functions, which could possibly have side effects. For example:
CL-USER> (defparameter *v* #((a 10) (b 20) (a 30) (b 40)))
*V*
CL-USER> (defun verbose-first (x) (format t 'Looking at ~s~%' x) (first x))
VERBOSE-FIRST
CL-USER> (count 'a *v* :key #'verbose-first)
Looking at (A 10)
Looking at (B 20)
Looking at (A 30)
Looking at (B 40)
2
CL-USER> (count 'a *v* :key #'verbose-first :from-end t)
Looking at (B 40)
Looking at (A 30)
Looking at (B 20)
Looking at (A 10)
2
Table 11-2 summarizes these arguments.