Comparison Procedures

Overview

Comparison procedures test relationships between objects and return a boolean value. This implementation provides three distinct categories of comparison: numeric ordering, object equality, and type-specific equality, each suited to different circumstances.

Numeric Ordering

The numeric ordering procedures — =, <, >, <=, and >= — compare numbers by value and accept any number of arguments, testing the relationship between each consecutive pair. They operate across the full numeric tower, automatically promoting arguments to a common type before comparing. = additionally accepts complex arguments, since complex numbers support equality testing even though they have no natural ordering; the remaining four require real arguments. These procedures are discussed in full in the Numerics section.

Object Equality

The three general equality predicates form a hierarchy of generality and computational cost, and choosing the right one for a given situation is important both for correctness and efficiency.

eq? is the most primitive and most efficient. It tests object identity — two objects are eq? only if they are the exact same object in memory. This gives reliable results for symbols (which are interned and therefore unique), booleans, the empty list, and any object compared with itself. It should not be used for numbers, characters, or compound objects, where distinct objects with identical values are not guaranteed to be pointer-identical.

eqv? extends eq? to cover value equality for atomic types: two numbers are eqv? if they have the same type, exactness, and value; two characters are eqv? if they have the same Unicode scalar value. For all other types, eqv? falls back to pointer identity. Note that eqv? is stricter than = for numbers: (eqv? 1 1.0) returns #f because the two arguments differ in type and exactness, even though (= 1 1.0) returns #t.

equal? is the most general predicate and performs deep structural comparison. Two lists are equal? if they have the same length and all corresponding elements are equal? recursively; likewise for vectors and bytevectors. Two strings are equal? if they contain the same sequence of characters. For numbers, equal? applies the same type-and-exactness requirement as eqv?. For all other atomic types it behaves identically to eqv?. Because equal? recurses into compound structures, it is necessarily more expensive than eq? or eqv? for large objects. It correctly handles circular and shared structure and will not loop infinitely on cyclic lists.

As a rule of thumb: use eq? when comparing symbols or testing object identity; use eqv? when comparing numbers or characters where type matters; use equal? when comparing compound data structures by content.

Comparison Procedures

Numeric Comparison Procedures

equal (=)

(= z1 z2 ...)

Returns #t if all arguments are numerically equal, #f otherwise. Unlike eqv? and equal?, this procedure compares by numeric value across types, so (= 1 1.0 1/1) returns #t. Accepts complex number arguments in addition to real numbers.

Parameters:

z1 (number) – Two or more numbers to compare.

Returns:

#t if all arguments are numerically equal, #f otherwise.

Return type:

boolean

Example:

--> (= 1 1)
#t
--> (= 1 1.0)
#t
--> (= 1 1/1)
#t
--> (= 1 2)
#f
--> (= 1+0i 1)
#t
--> (= 1 1 1 1)
#t

greater than (>)

(> x1 x2 ...)

Returns #t if the arguments are monotonically decreasing, #f otherwise. Arguments must be real numbers; complex numbers are not permitted.

Parameters:

x1 (real) – Two or more real numbers to compare.

Returns:

#t if the arguments are monotonically decreasing, #f otherwise.

Return type:

boolean

Example:

--> (> 3 2)
#t
--> (> 2 3)
#f
--> (> 3 2 1)
#t
--> (> 3 3)
#f
--> (> 1/2 1/3)
#t

less than (<)

(< x1 x2 ...)

Returns #t if the arguments are monotonically increasing, #f otherwise. Arguments must be real numbers; complex numbers are not permitted.

Parameters:

x1 (real) – Two or more real numbers to compare.

Returns:

#t if the arguments are monotonically increasing, #f otherwise.

Return type:

boolean

Example:

--> (< 1 2)
#t
--> (< 2 1)
#f
--> (< 1 2 3)
#t
--> (< 1 1)
#f
--> (< 1/3 1/2)
#t

greater than or equal (>=)

(>= x1 x2 ...)

Returns #t if the arguments are monotonically non-increasing, #f otherwise. Arguments must be real numbers; complex numbers are not permitted.

Parameters:

x1 (real) – Two or more real numbers to compare.

Returns:

#t if the arguments are monotonically non-increasing, #f otherwise.

Return type:

boolean

Example:

--> (>= 3 2)
#t
--> (>= 3 3)
#t
--> (>= 2 3)
#f
--> (>= 3 2 2 1)
#t

less than or equal (<=)

(<= x1 x2 ...)

Returns #t if the arguments are monotonically non-decreasing, #f otherwise. Arguments must be real numbers; complex numbers are not permitted.

Parameters:

x1 (real) – Two or more real numbers to compare.

Returns:

#t if the arguments are monotonically non-decreasing, #f otherwise.

Return type:

boolean

Example:

--> (<= 1 2)
#t
--> (<= 1 1)
#t
--> (<= 2 1)
#f
--> (<= 1 1 2 3)
#t

General Comparison Procedures

eq?

(eq? obj1 obj2)

Returns #t if obj1 and obj2 are the exact same object, #f otherwise. Comparison is by pointer identity — two objects are eq? only if they occupy the same location in memory.

eq? is the most efficient equality predicate but the least general. It gives reliable results for symbols, booleans, the empty list, and other unique objects. It should not be used to compare numbers, characters, strings, pairs, or vectors, since two distinct objects with the same value are not guaranteed to be pointer-identical.

Parameters:
  • obj1 (any) – The first object to compare.

  • obj2 (any) – The second object to compare.

Returns:

#t if obj1 and obj2 are the same object, #f otherwise.

Return type:

boolean

Example:

--> (eq? 'foo 'foo)
#t
--> (eq? '() '())
#t
--> (eq? #t #t)
#t
--> (let ((x '(1 2))) (eq? x x))
#t
--> (eq? '(1 2) '(1 2))
#f
--> (eq? "hello" "hello")
#f

eqv?

(eqv? obj1 obj2)

Returns #t if obj1 and obj2 are equivalent, #f otherwise. eqv? extends eq? by additionally considering numbers of the same type and value, and characters with the same Unicode scalar value, as equivalent — even if they are distinct objects in memory.

For types not covered by these rules (pairs, vectors, strings, and other compound objects), eqv? falls back to pointer identity. Use equal? for deep structural comparison of compound objects.

Note that eqv? requires both objects to be of the same type before comparing values, so (eqv? 1 1.0) returns #f even though (= 1 1.0) returns #t.

Parameters:
  • obj1 (any) – The first object to compare.

  • obj2 (any) – The second object to compare.

Returns:

#t if obj1 and obj2 are equivalent, #f otherwise.

Return type:

boolean

Example:

--> (eqv? 42 42)
#t
--> (eqv? 1 1.0)
#f
--> (eqv? #\a #\a)
#t
--> (eqv? 'foo 'foo)
#t
--> (eqv? '() '())
#t
--> (eqv? '(1 2) '(1 2))
#f

equal?

(equal? obj1 obj2)

Returns #t if obj1 and obj2 have the same structure and contents, #f otherwise. equal? performs a deep recursive comparison: two lists are equal? if they have the same length and each corresponding pair of elements is equal?; two vectors are equal? if they have the same length and each corresponding pair of elements is equal?; two strings are equal? if they contain the same sequence of characters; two bytevectors are equal? if they are of the same type and contain the same sequence of elements.

For numbers, equal? requires both the same type and the same exactness, so (equal? 2 2/1) returns #f even though (= 2 2/1) returns #t. For all other atomic types, equal? behaves like eqv?.

equal? correctly handles circular and shared list structures using cycle detection, and will not loop infinitely on circular lists.

Parameters:
  • obj1 (any) – The first object to compare.

  • obj2 (any) – The second object to compare.

Returns:

#t if obj1 and obj2 are structurally equal, #f otherwise.

Return type:

boolean

Example:

--> (equal? '(1 2 3) '(1 2 3))
#t
--> (equal? '(1 2 3) '(1 2 4))
#f
--> (equal? #(1 2 3) #(1 2 3))
#t
--> (equal? "hello" "hello")
#t
--> (equal? 2 2/1)
#f
--> (equal? '(1 (2 3)) '(1 (2 3)))
#t