Search code examples
recursionschemeracketequalityr5rs

Recursive numeric equality in Scheme


It seems that Scheme considers integer and floating point versions of a number to be different when using equal?, but the same when using = to compare them:

(equal? 2 2.0)  ; => #f
(= 2 2.0)       ; => #t

However, if I have a recursive structure with some numeric parts (or even a simple list of numbers), is there a method to compare them that uses = for numeric comparisons?

(equal? '(2 3) '(2.0 3.0))  ; => #f
(= '(2 3) '(2.0 3.0))       ; error: contract violation

I can write my own equality checker, something like this:

(define myequal?
  (lambda (x y)
    (cond ((and (null? x) (null? y)) #t)
          ((or (null? x) (null? y)) #f)
          ((and (pair? x) (pair? y))
           (and (myequal? (car x) (car y))
                (myequal? (cdr x) (cdr y))))
          ((or (pair? x) (pair? y)) #f)
          ((and (number? x) (number? y)) (= x y))
          ((or (number? x) (number? y)) #f)
          (else (equal? x y)))))

But it seems like this would be a common enough task that Scheme might have a builtin method to do this.


Solution

  • In Racket you can build the notion of equality that you want with the help of the equal?/recur built-in procedure:

    ;; equalish? : Any Any -> Boolean
    ;; Like equal?, but use = for numbers (including within compound data)
    (define (equalish? a b)
      (if (and (number? a) (number? b))
          (= a b)
          (equal?/recur a b equalish?)))
    
    (equalish? '(2 3) '(2.0 3.0))
    ;; => #t
    

    The equal?/recur procedure handles recurring through pairs, structures, etc.