Skip to content

LISP - String Comparison

Standard numeric operators like < and > do not work for comparing strings in Common Lisp. Instead, the language provides dedicated sets of functions for both case-sensitive and case-insensitive string comparison.

These functions are more powerful than simple equality checks, as they can compare substrings and operate on sequences of strings.

Case-SensitiveCase-InsensitiveDescription
string=string-equalReturns T if the strings are identical, NIL otherwise.
string/=string-not-equalReturns the index of the first mismatch if strings differ, NIL otherwise.
string<string-lesspReturns mismatch index if string1 is lexicographically less than string2, else NIL.
string>string-greaterpReturns mismatch index if string1 is lexicographically greater than string2, else NIL.
string<=string-not-greaterpReturns mismatch index if string1 is not greater than string2, else NIL.
string>=string-not-lesspReturns mismatch index if string1 is not less than string2, else NIL.

Key Concept: Many comparison functions return a “generalized boolean”. NIL is always false. Any other value, including a number like the mismatch index 0, is considered true.

Case-sensitive functions perform a character-by-character comparison based on their character codes. Uppercase letters have different codes than lowercase letters.

(format t "'test' = 'Test' -> ~a~%" (string= "test" "Test"))
;; "test" is lexicographically greater than "Test" because 't' > 'T'.
;; The function returns the index of the first character mismatch.
(format t "'test' > 'Test' -> ~a~%" (string> "test" "Test"))
;; "apple" is less than "banana". Mismatch is at index 1 ('p' vs 'a').
(format t "'apple' < 'banana' -> ~a~%" (string< "apple" "banana"))

When you execute the code, it returns the following result:

'test' = 'Test' -> NIL
'test' > 'Test' -> 0
'apple' < 'banana' -> 1

Case-insensitive functions are generally preferred for user input or text that should be compared semantically, ignoring capitalization.

(format t "'test' equal 'Test' -> ~a~%" (string-equal "test" "Test"))
;; When ignoring case, "test" is not greater than "Test".
(format t "'test' greaterp 'Test' -> ~a~%" (string-greaterp "test" "Test"))
;; 'apple' is still less than 'banana' when ignoring case.
(format t "'apple' lessp 'banana' -> ~a~%" (string-lessp "apple" "banana"))

The results now ignore case:

'test' equal 'Test' -> T
'test' greaterp 'Test' -> NIL
'apple' lessp 'banana' -> 1

Comparing Substrings with Start/End Indices

Section titled “Comparing Substrings with Start/End Indices”

All string comparison functions accept optional :start and :end keyword arguments for each string, allowing you to compare specific portions of strings.

;; Compare the substring "world" from the first string with the second string.
(let ((s1 "hello world")
(s2 "world"))
(format t "Comparing substring: ~a~%"
(string-equal s2 s1 :start2 6)))

The comparison succeeds because we are only looking at the relevant part of s1:

Comparing substring: T