Lisp has a few operators (or functions, or predicates, whatever you prefer to call them) for equality testing, which can be very confusing. This article's aim is to clarify the uses of, and emphasize the differences between eq, eql, equal, equalp and =. I try to provide plenty of examples, to cover most of the useful cases where equality operators are applied.

The simplest equality operator to explain is =. It just compares numbers, and refuses to work on other types. Numbers of different types may be compared with =, for instance 3.0 is = to 3 (you'll shortly see why I'm noting this seemingly obvious fact). Examples:

(= 4 4.0) 
=> T

(= #c(4 5) #c(4 6))
=> NIL

(= 4.0 #c(4.0 0))
=> T

(= 6/4 1.5)
=> T 

(= 'r 't)
=> Error: argument to = should be a number

Now we get to the tricky stuff. eq returns true iff the objects it compares are the same object (under the hood, the memory addresses are compared). This is best shown with an example:

(setq x (cons 'a 'b))
=> (A . B)

(setq y x)
=> (A . B)

(eq x y)
=> T

(eq (cons 'a 'b) (cons 'a 'b))
=> NIL
x and y are eq because they point to the same object (setq just copies the pointer, not the actual memory, so x and y are two pointers that point to the same memory). On the other hand, two separate calls of (cons 'a 'b) are not eq because a separate memory chunk is allocated for each. However, take a look at this:
(eq 'a 'b)
=> NIL

(eq 'a 'a)
=> T
Why is the second comparison true ? It is so because internally Lisp doesn't really duplicate symbols. The symbol is created in memory only on the first time the reader sees it. Comparing numbers and characters with eq is tricky. On some implementations the following:
(eq 1 1)
Will be true, and on some false (it is not specified in the standard whether an implementation should keep just one copy of numbers and chars in memory, like it does with symbols). Additionally, eq is sensitive to the types of numbers (unlike =). That is, 4.0 is not eq to 4. As a rule of thumb don't use eq on numbers and characters, unless you really know what you're doing.

eql behaves mostly like eq, but it assures that numbers with the same type and value are eql. The same applies with characters (their comparison is case ensitive). eql is the default equality test in Common Lisp (for operators that take sequences as arguments).

(eql 4 4)
=> T

(eql 4 4.0)    ; same value, but different type
=> NIL

(eql #\d #\d)   
=> T

(eql #\d #\D)
=> NIL

(eql (cons 'a 'b) (cons 'a 'b))
=> NIL
equal is a "saner" comparison function. As a rule of thumb, you can think of it as telling you whether two objects look the same (structurally similar, or isomorphic). It is probably the operator you want to use for general equality. It behaves like eql for numbers, characters and symbols, but for lists (conses) and strings it tells if their elements are the same, regardless of where they are allocated.
(equal (cons 'a 'b) (cons 'a 'b))
=> T

(equal (list 1 2 3) (list 1 2 3))
=> T

(equal "johny" "johny")
=> T
As I said, for numbers and characters equal behaves like eql. Therefore:
(equal 42 42.0)
=> NIL

(equal "tornado" "Tornado")
=> NIL
However, strings are the only arrays equal works for. For other arrays it uses b>eq. The same is true for hashes and structures. Examples:
(equal (vector 1 2 3) (vector 1 2 3))
=> NIL

(equal (make-my-struct :color 'red :size 24) 
         (make-my-struct :color 'red :size 24))
=> NIL
equalp is equal's lenient and advanced brother. It is lenient in the sense that = (type insensitive) is used for numbers, and characters are compared case insensitively:
(equalp 42 42.0)
=> T

(equalp "tornado" "Tornado")
=> T
It is advanced in the sense that it knows how to compare more general arrays, hashes and structures:
(equalp (vector 1 2 3) (vector 1 2 3))
=> T

(equalp (make-my-struct :color 'red :size 24) 
           (make-my-struct :color 'red :size 24))
=> T
What all this plethora of comparison options is good for, you may ask. The answer is - flexibility. Other than differing in their abilities, the Lisp equality operators differ in their efficiency. As you can guess, eq is the fastest. It's only a pointer comparison: nothing smart. The other operators have special cases, hence they are slower. On the other hand, eql is sufficiently fast in most cases - in fact, it's the default equality operator used in Lisp's sequence functions.

Summary

Lisp's equality operators are:
  1. = compares only numbers, regardless of type.
  2. eq compares symbols. Two objects are eq iff they are actually the same object in memory. Don't use it for numbers and characters.
  3. eql compares symbols similarly to eq, numbers (type sensitive) and characters (case sensitive)
  4. equal compares more general objects. Two objects are equal iff they are eql, strings of eql characters, bit vectors of the same contents, or lists of equal objects. For anything else, eq is used.
  5. equalp is like equal, just more advanced. Comparison of numbers is type insensitive. Comparison of chars and strings is case insensitive. Lists, hashes, arrays and structures are equalp if their members are equalp. For anything else, eq is used.


Comments

comments powered by Disqus