Skip to content

LISP - position of Element

Common Lisp provides the versatile position function to find the index of an element within a sequence (like a list or a vector). The first index of a sequence is 0.

(position item sequence &key :from-end :test :key :start :end)
  • item: The element to search for.
  • sequence: The list, vector, or string to search in.
  • Returns: The zero-based index of the first matching element found. If the item is not found, it returns NIL.
;; Find the position of the symbol 'c
(position 'c '(a b c d e c)) ; -> 2
;; Search for an item that doesn't exist
(position 'z '(a b c d e)) ; -> NIL
;; The syntax for a vector is #( ... )
(position #\d #(#\a #\b #\c #\d)) ; -> 3

Customizing the Search with Keyword Arguments

Section titled “Customizing the Search with Keyword Arguments”

The power of position comes from its optional keyword arguments.

  • :from-end t: Starts the search from the end of the sequence instead of the beginning.
  • :start index: Starts the search at the specified index.
  • :end index: Stops the search before the specified index.
  • :test function: A function used to compare the item with sequence elements. This is crucial for non-default equality checks.
  • :key function: A function applied to each element of the sequence before the comparison happens.
;; Find the last occurrence of 'c
(position 'c '(a b c d c f) :from-end t) ; -> 4
;; Find 'c' starting from index 3
(position 'c '(a b c d c f) :start 3) ; -> 4

A Deeper Look: The :test Argument and Equality

Section titled “A Deeper Look: The :test Argument and Equality”

A common pitfall for beginners is not understanding Lisp’s different equality functions. The default :test for position is #'eql, which may not be what you want for comparing strings or lists.

  • eq: Compares memory addresses. True only for the exact same object.
  • eql (the default): Like eq, but also true for identical characters and numbers of the same type.
  • equal: Compares values recursively. True if strings have the same characters or lists have the same equal elements.
  • equalp: A more permissive equal. Ignores case in strings and compares numeric values regardless of type (e.g., (equalp 2 2.0) is true).
;; This returns NIL because the two "foo" strings are different objects.
(position "foo" '("bar" "foo" "baz")) ; -> NIL
;; This works because `equal` compares string content.
(position "foo" '("bar" "foo" "baz") :test #'equal) ; -> 1
;; Find a sublist
(position '(1 2) '((1 1) (1 2) (1 3)) :test #'equal) ; -> 1

The :key argument is incredibly useful for searching inside complex data structures. It lets you specify what part of each element to look at.

Example: Searching a list of property lists (plists)

Section titled “Example: Searching a list of property lists (plists)”
(defvar *people*
'((:name "Alice" :age 30)
(:name "Bob" :age 25)
(:name "Charlie" :age 35)))
;; Find the position of the person named "Bob".
;; The :key extracts the name from each person's plist before comparison.
(position "Bob" *people*
:key #'(lambda (person) (getf person :name))
:test #'string-equal) ; string-equal is a case-insensitive equal
1