LISP - Searching a List
Lisp - Searching in Lists
Section titled “Lisp - Searching in Lists”Common Lisp offers a comprehensive suite of functions for searching within lists and other sequences. Understanding these tools is key to processing data effectively. The functions are highly flexible, allowing searches based on value, position, or arbitrary conditions.
Searching by Value: find and position
Section titled “Searching by Value: find and position”The most basic search operations look for a specific item in a list. find returns the item itself, while position returns its zero-based index.
Syntax
Section titled “Syntax”(find item sequence &key :test :key)(position item sequence &key :test :key)Key Arguments
Section titled “Key Arguments”item: The element to search for.sequence: The list (or other sequence) to be searched.:test: A function used for comparison. Defaults to#'eql. Use#'equalfor strings and#'equalpfor case-insensitive string comparison.:key: A function to be applied to each element of the sequence before the comparison is made.
Return Value
Section titled “Return Value”Both functions return the desired result (the item or its index) on the first match. If no match is found, they return NIL.
Example: Using find and position
Section titled “Example: Using find and position”(let ((my-list '(10 20 30 40 50)) (people '((:name "Alice" :age 30) (:name "Bob" :age 25))))
;; Simple search: find the item 30 (format t "Find 30: ~a~%" (find 30 my-list))
;; Simple search: find the position of 30 (format t "Position of 30: ~a~%" (position 30 my-list))
;; Search for a non-existent item (format t "Position of 99: ~a~%" (position 99 my-list))
;; Advanced search: find the person struct with name "Bob" ;; :key extracts the :name value before comparing with "Bob" ;; :test must be `equal` to compare strings (let ((bob (find "Bob" people :key #' (lambda (p) (getf p :name)) :test #'equal))) (format t "Found person: ~a~%" bob)))Output
Section titled “Output”Find 30: 30Position of 30: 2Position of 99: NILFound person: (:NAME "Bob" :AGE 25)Searching by Condition: find-if and position-if
Section titled “Searching by Condition: find-if and position-if”Often, you don’t want to find a specific value, but rather the first element that satisfies a certain condition. This is where find-if and its relatives shine. They take a predicate function as an argument.
Syntax
Section titled “Syntax”(find-if predicate sequence &key :key)(position-if predicate sequence &key :key)Arguments
Section titled “Arguments”predicate: A function that accepts one argument and returnsT(true) orNIL(false). The search looks for the first element for which the predicate returnsT.sequence: The list to be searched.
Returns
Section titled “Returns”find-if returns the first matching element, while position-if returns its index. Both return NIL if no element satisfies the predicate.
Example: Finding the First Odd Number
Section titled “Example: Finding the First Odd Number”(let ((numbers '(2 4 6 3 7 8)))
;; `oddp` is a built-in predicate that returns T if a number is odd. (format t "First odd number: ~a~%" (find-if #'oddp numbers))
(format t "Position of first odd number: ~a~%" (position-if #'oddp numbers))
;; You can also use `find-if-not` to find the first element that *doesn't* satisfy the predicate. (format t "First number that is not even: ~a~%" (find-if-not #'evenp numbers)))Output
Section titled “Output”First odd number: 3Position of first odd number: 3First number that is not even: 3Choosing the Right Search Function
Section titled “Choosing the Right Search Function”Here’s a quick guide to help you decide which function to use:
- To get an item back: Use
find(for a specific value) orfind-if(for a condition). - To get an index: Use
positionorposition-if. - Just to check for existence:
member(ormember-if) is often more idiomatic. It returns the rest of the list starting from the found element, which is a true value, orNILif not found. For example:(if (member 30 my-list) ...).
Common Pitfalls
Section titled “Common Pitfalls”- Forgetting
:testfor strings: When searching for a string in a list, the default test#'eqlwill usually fail because it checks for object identity, not structural equality. Always use(find "my-string" list :test #'equal). - Confusing
findandfind-if: Remember thatfindtakes an item as its first argument, whilefind-iftakes a function (a predicate).