Higher-Order Operations


Searching lists: find

Basics - The simplest case is just (find Entry List), usually used as a predicate: "is Entry in List?". If it succeeds in finding the element in question, it returns the first matching element instead of just "t".

(if (find 3 '(1 2 3 4)) 'Yes 'No) ==> YES
(find 4 '(1 2 3 4 5)) ==> 4

:test - Tells Lisp what predicate to use to when comparing the entry you supplied to each element of the list (default is "eql"). Thus
(find Entry List :test Predicate) means "is there an element of List such that Entry and that element are related by Predicate? If so, return the first such element". For instance
(find 3 '(2 4 6 8) :test #'<)
means "is there an element of (2 4 6 8) such that (< 3 element) returns true? If so, return the first one", and would return 4. Remember that lists and strings are not comparable by "eql", so you need to use a :test of #'equal if you want to look for lists or strings embedded in another list.

(find '(A B)'((A B) (C D) (E F)) ==> NIL
(find '(A B)'((A B) (C D) (E F) :test #'equal)
  ==> (A B)
(defun Almost-Equal (Num1 Num2)
  (<= (abs (- Num1 Num2)) 0.1))
(find pi '(2.9 3.0 3.1 3.2 3.3) 
      :test #'Almost-Equal) ==> 3.1

:key - Tells Lisp what part of each element of the list to compare to the supplied entry. Thus (find Entry List :key Function) means "is there an element of List such that Entry is eql to (Function element)? If so, return the first such element." For instance,
(find 2 '((1 2) (2 3) (3 4)) :key #'second) means "find an element of ((1 2) (2 3) (3 4)) such that 2 is eql to second of that element", and would return (1 2). :test may be combined with :key.

(find A '((A B) (C D E) (F G H I))
        :key #'first) ==> (A B)
(find 3 '((A B) (C D E) (F G H I)) 
      :key #'length) ==> (C D E)
(find '(D E) '((A B) (C D E) (F G H I))
      :key #'rest :test #'equal) ==> (C D E)

find-if/find-if-not

The "find-if" function is used to find an element of list that satisfies some predicate. It takes a :key arg, but not :test since the test is now a required argument.

(find-if #'evenp '(1 2 3 4)) ==> 2
(find-if #'oddp '((0 1) (1 2) (2 3))
         :key #'first) ==> (1 2)
The "find-if-not" function finds the first element of the list that fails a given predicate.

Alternatives to find: assoc/member

The "assoc" function is like "find" with a built-in :key of #'first. The "member" function is like "find" except that, if a match is found, it returns the entire target list starting at the match, rather than just the match itself. There are also analogous "assoc-if" and "member-if" functions.

(assoc 'B '((A B) (B C) (C D)) ==> (B C)
(member 2 '((0 1) (1 2) (2 3)) :key #'second)
  ==> ((1 2) (2 3))

Filtering lists: remove/remove-if

The "remove" function returns a new list that is like the input list, except with all occurrences of the supplied entry removed. It takes :test and :key options in a manner similar to "find".

(remove 1 '((0 1) (1 2) (2 3)) :key #'first)
  ==> ((0 1) (2 3))
The "remove-if" function returns a new list that was like the old list except that all elements satisfying a given predicate are removed. Like "find-if", it takes a :key option, but not :test. There is an analogous "remove-if-not".

(remove-if #'evenp '(1 2 3 4 5)) ==> (1 3 5)
(remove-if-not #'evenp '(1 2 3 4 5)) ==> (2 4)

Transforming lists: mapcar

The basic case of "mapcar" is that it takes a function of one argument and a list. It applies the function to each element of the list, collects the results, and returns them.

(mapcar #'sqrt '(1 4 9 16 25)) ==> (1 2 3 4 5)
(mapcar #'first '((A B) (C) (D E))) ==> (A C D)

The slightly more complicated case of "mapcar" is that it takes an N-ary function (a function that expects N arguments) and N lists. It applies the function to the first elements of each of the lists, then the second, and so forth, collecting the results. Technically, it stops when the shortest list ends, but it is normally considered poor style to supply lists of differing lengths.

(mapcar #'+ '(1 2 3 4)
            '(5 6 7 8)) ==> (6 8 10 12)
(mapcar #'list '(A B C D)
               '(E F G H)) 
  ==> ((A E) (B F) (C G) (D H))

Calling functions explicitly: funcall/apply

The function "funcall" takes a function and individual arguments, and calls the function with the specified arguments. It is usually used when the particular function to be called is not known in advance, but is passed in (e.g. to implement the :test and :key arguments).

(funcall #'+ 1 2 3) ==> 6
(funcall #'append '(A B) '(C D) '(E F G))
  ==> (A B C D E F G)

The "apply" function is similar to "funcall", but it expects the arguments to come in a list, rather than individually. It is normally used when you have an indeterminate number of arguments.

(apply #'+ '(1 2 3)) ==> 6
(apply #'append '((A B) (C D) (E F G)))
  ==> (A B C D E F G)
If "apply" gets passed more than just a list of arguments, the extra values are attached to the front of the list.

(apply #'+ 5 '(1 2 3)) == (apply #'+ '(5 1 2 3))
  ==> 11
Since it seems to be more common to want to keep all elements that satisfy a given predicate than to remove all elements that satisfy a predicate, sometimes people define "Keep-If" as a renamed version of "remove-if-not":

(defun Keep-If (Pred Lst &rest Specifiers)
  (apply #'remove-if-not Pred Lst Specifiers))

Table of contents

Searching lists: find
find-if/find-if-not
Alternatives to find: assoc/member
Filtering lists: remove/remove-if
Transforming lists: mapcar
Calling functions explicitly: funcall/apply