LISP - Predicates
Lisp - Predicates
Section titled “Lisp - Predicates”In Common Lisp, a predicate is a function that tests for a certain condition. It returns a true or false value. A key concept in Lisp is its representation of truth: the symbol nil is the only false value, while any other value is considered true. By convention, the symbol t is used as the canonical true value.
Predicates are conventionally named with a -p or p suffix (the ‘p’ stands for ‘predicate’), like numberp or evenp.
Common Predicate Categories
Section titled “Common Predicate Categories”Here are some of the most frequently used predicates, grouped by their purpose.
Equality Predicates
Section titled “Equality Predicates”Choosing the right equality predicate is a common task in Lisp. They differ in how strictly they compare their arguments.
| Predicate | Description |
|---|---|
| eq | Returns t if the arguments are the exact same object in memory. It’s the strictest and fastest comparison. |
| eql | Like eq, but also returns t for identical numbers and characters. For example, (eql 5 5) is t, but (eql 5 5.0) is nil. |
| equal | Returns t if the arguments are structurally similar. It recursively compares lists and strings. For example, two different lists with the same elements are equal. |
| equalp | The most lenient comparison. Like equal, but ignores case in strings and compares numbers of different types (e.g., (equalp 5 5.0) is t). |
Type Predicates
Section titled “Type Predicates”| Predicate | Description |
|---|---|
| atom | Returns t if the argument is an atom (i.e., not a cons cell/list). |
| listp | Returns t if the argument is a list (a cons cell or nil). |
| numberp | Returns t if the argument is any kind of number. |
| integerp | Returns t if the argument is an integer. |
| floatp | Returns t if the argument is a floating-point number. |
| stringp | Returns t if the argument is a string. |
| symbolp | Returns t if the argument is a symbol. |
| null | Returns t if the argument is nil, and nil otherwise. |
Numeric Predicates
Section titled “Numeric Predicates”| Predicate | Description |
|---|---|
| zerop | Takes one numeric argument and returns t if it is zero. |
| evenp | Takes one integer argument and returns t if it is even. |
| oddp | Takes one integer argument and returns t if it is odd. |
| > | Takes one or more numbers. Returns t if they are in strictly decreasing order. |
| < | Takes one or more numbers. Returns t if they are in strictly increasing order. |
| = | Takes one or more numbers. Returns t if they are all numerically equal. |
Code Example: Predicate in Action
Section titled “Code Example: Predicate in Action”Let’s create a source file main.lisp to demonstrate these concepts. This example highlights the differences between equality predicates and shows some common type checks.
main.lisp
Section titled “main.lisp”;;; main.lisp - Demonstrating Common Lisp Predicates
(defun run-predicate-demos () "Runs a series of demonstrations for predicate functions."
(format t "--- Equality Predicate Demos ---~%") (let ((list1 (list 'a 'b)) (list2 (list 'a 'b))) (format t "(eq list1 list2) -> ~a (Different objects in memory)~%" (eq list1 list2)) (format t "(equal list1 list2) -> ~a (Structurally equivalent)~%~%" (equal list1 list2)))
(format t "--- Numeric Predicate Demos ---~%") (format t "(oddp 7) -> ~a~%" (oddp 7)) (format t "(evenp 10) -> ~a~%" (evenp 10)) (format t "(zerop 0.0) -> ~a~%" (zerop 0.0)) (format t "(= 5 5.0) -> ~a (Numeric equality)~%" (= 5 5.0)) (format t "(> 10 5 1) -> ~a~%" (> 10 5 1)) (format t "(< 2 4 8) -> ~a~%~%" (< 2 4 8))
(format t "--- Type Predicate Demos ---~%") (format t "(listp '(1 2 3)) -> ~a~%" (listp '(1 2 3))) (format t "(atom 'my-symbol) -> ~a~%" (atom 'my-symbol)) (format t "(stringp \"Hello\") -> ~a~%" (stringp "Hello")) (format t "(null nil) -> ~a~%" (null nil)))
;; To run this code, load the file in a Lisp environment and call (run-predicate-demos)(run-predicate-demos)Output
Section titled “Output”When you load and execute this code in a Common Lisp implementation (like SBCL), you will see:
--- Equality Predicate Demos ---(eq list1 list2) -> NIL (Different objects in memory)(equal list1 list2) -> T (Structurally equivalent)
--- Numeric Predicate Demos ---(oddp 7) -> T(evenp 10) -> T(zerop 0.0) -> T(= 5 5.0) -> T (Numeric equality)(> 10 5 1) -> T(< 2 4 8) -> T
--- Type Predicate Demos ---(listp '(1 2 3)) -> T(atom 'my-symbol) -> T(stringp "Hello") -> T(null nil) -> TPractical Application: Factorial with zerop
Section titled “Practical Application: Factorial with zerop”Predicates are fundamental for controlling program flow. Here is a classic recursive factorial function that uses the zerop predicate as its base case.
factorial.lisp
Section titled “factorial.lisp”;;; factorial.lisp - A recursive factorial implementation
(defun factorial (n) "Computes the factorial of a non-negative integer N." (check-type n (integer 0 *) "N must be a non-negative integer.") (if (zerop n) 1 ; Base case: 0! = 1 (* n (factorial (1- n))))) ; Recursive step: n * (n-1)!
;; Example usage:(let ((num 6)) (format t "Factorial of ~d is: ~d~%" num (factorial num)))Output
Section titled “Output”Executing this code produces:
Factorial of 6 is: 720Best Practices & Further Learning
Section titled “Best Practices & Further Learning”- Choosing Equality: Use
eqfor symbol comparison,eqlas a general default,equalfor comparing lists or sequences, andequalpwhen you need flexible, case-insensitive, or type-insensitive comparisons. - Numeric Comparison: Always use
=for comparing numbers, noteqlorequal, as=correctly handles different numeric types (e.g., integers and floats). - Error Checking: Use predicates for robust error checking. The
check-typemacro in the factorial example is a powerful way to enforce that arguments have the correct type.