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 #\b "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 item to each element instead of the default object equality test, 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 key value, which will then be compared to the item in the place of the element itself. Note, however, that functions such as 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.
Table 11-2. Standard Sequence Function Keyword Arguments
| Argument | Meaning | Default |
:test |
Two-argument function used to compare item (or value extracted by :key function) to element. |
EQL |
:key |
One-argument function to extract key value from actual sequence element. NIL means use element as is. |
NIL |
:start |
Starting index (inclusive) of subsequence. | 0 |
:end |
Ending index (exclusive) of subsequence. NIL indicates end of sequence. |
NIL |
:from-end |
If true, the sequence will be traversed in reverse order, from end to start. | NIL |
:count |
Number indicating the number of elements to remove or substitute or NIL to indicate all (REMOVE and SUBSTITUTE only). |
NIL |
Higher-Order Function Variants
For each of the functions just discussed, Common Lisp provides two higher-order function variants that, in the place of the item argument, take a function to be called on each element of the sequence. One set of variants are named the same as the basic function with an -IF appended. These functions count, find, remove, and substitute elements of the sequence for which the function argument returns true. The other set of variants are named with an -IF-NOT suffix and count, find, remove, and substitute elements for which the function argument does not return true.
(count-if #'evenp #(1 2 3 4 5)) ==> 2
(count-if-not #'evenp #(1 2 3 4 5)) ==> 3
(position-if #'digit-char-p "abcd0001") ==> 4
(remove-if-not #'(lambda (x) (char= (elt x 0) #\f))
#("foo" "bar" "baz" "foom")) ==> #("foo" "foom")
According to the language standard, the -IF-NOT variants are deprecated. However, that deprecation is generally considered to have itself been ill-advised. If the standard is ever revised, it's more likely the deprecation will be removed than the -IF-NOT functions. For one thing, the REMOVE-IF-NOT variant is probably used more often than REMOVE-IF. Despite its negative-sounding name, REMOVE-IF-NOT is actually the positive variant—it returns the elements that do satisfy the predicate.[125]
123
Another parameter, :test-not parameter, specifies a two-argument predicate to be used like a :test argument except with the boolean result logically reversed. This parameter is deprecated, however, in preference for using the COMPLEMENT function. COMPLEMENT takes a function argu-ment and returns a function that takes the same number of arguments as the original and returns the logical complement of the original function. Thus, you can, and should, write this:
(count x sequence :test (complement #'some-test))
rather than the following:
(count x sequence :test-not #'some-test)
124
Note, however, that the effect of :start and :end on REMOVE and SUBSTITUTE is only to limit the elements they consider for removal or substitution; elements before :start and after :end will be passed through untouched.