Common Lisp, like other languages has a set of rules on how to decide if two objects are the 'same'. These rules define four levels, each with a function that performs that level of checking. The levels are ordered from strictest to loosest.
eq
The first level is object identity.
This equality is checked with the function eq
.
The two objects being checked for equality must be the very same object:
(eq 'apples 'apples) ; => T
(eq 'apples 'oranges) ; => NIL
(eq '(a b c) '(a b c) ; => NIL (these two lists have the same contents but are not the same list)
(let ((list1 '(a b c)) (list2 list1))
(eq list1 list2)) ; => T (these two lists are the same list)
eql
The second level adds equality of numbers and characters.
This equality is checked with the function eql
.
The way the checking is done depends upon the types of the arguments:
eq
are eql
eql
if they are of the same type and valueeql
if they they represent the same character.(eql 1 1) ; => T
(eql 1 1/1) ; => NIL (one number is an integer, the other a rational)
(eql #\c #\c) ; => T
(eql #\c #\C) ; => NIL (case is different)
One may wonder why numbers and characters are not compared for object identity with eq
.
The Common Lisp standard allows implementations to copy numbers and characters if they choose to do so.
Thus 0
and 0
may not be eq
as they may be different instances of the number 0
.
equal
The third level checks for structural similarity.
This equality is checked with equal
.
The way the checking is done depends upon the types of the arguments:
eq
eql
equal
if their elements are equal
.
This is done recursively.equal
if their elements are eql
eq
equal
if they are functionality equivalent.
(There is room for implementation dependent behavior here with regards to case sensitivity of the strings which make up the components of the pathnames.)eq
(equal '(a (b c)) '(a (b c))) ; => T (conses are equal if their contents are equal)
(equal "hello" "hello") ; => T
(equal "hello" "HELLO") ; => NIL
(equal #(1 2 3) #(1 2 3)) ; => NIL (arrays are equal only if eq)
(equal #P"foo/bar.md" #P"foo/bar.md") ; => T (pathnames are equal if "functionally equivalent"
equalp
The fourth and most loose level of equality is checked with equalp
.
The how the checking is done depends upon the types:
equalp
then they are equalp
equalp
if they have the same value even if they are not of the same typeequalp
if their elements are equalp
.
This is done recursively.equalp
if they have the same number of dimensions, those dimensions are the same, and each element is equalp
.equalp
if they have the same class and slots and each of those slots are equalp
between the two structures.equalp
if they both have the same :test
function, they have the same keys (as compared with that :test
function) and that those keys have the same values as compared with equalp
.(equalp 1 1.0) ; => T
(equalp #\c #\C) ; => T
(equalp "hello" "HELLO") ; => T
(equalp #(1 2 3) #(1.0 2.0 3.0)) ; => T (arrays contain elements which are `equalp`)
(equal #S(TEST :SLOT1 'a :SLOT2 'b)
#S(TEST :SLOT1 'a :SLOT2 'b)) ; => T (structures of the same class with slots that have values which are `equalp`)
The above are the 'generic' equality functions.
They work, as defined, for any type.
This can be useful when one writes generic code that does not know the types of objects it will be comparing until run-time.
However it is generally considered "better style" to use type specific equality functions when one knows the types being compared.
For example string=
rather than equal
.
These functions will be presented and discussed in the relevant concepts.