1 Equivalence Relations
(require relation/equivalence) | package: Relation |
A generic interface and utilities for comparing data.
By default, the built-in equivalence operator = operates on numbers specifically, while the operators eq?, eqv? and equal? are more suitable for other comparisons depending on the type of the values being compared. Additionally, there are type-specific comparison operators, for instance char=? and string=?, that may be used if the type is known.
This module provides a generic interface that overrides the standard = operator to allow its use with any comparable type and not only numbers, performing the most appropriate comparison depending on the type of the values being compared. It also supports additional parameters to express broader notions of equivalence than simple equality. You can also provide an implementation for the interface in custom types so that they can be compared using the same standard equality operator and the generic utilities available in this module.
1.1 Interface
value
> (= 1 1) #t
> (= 1 2) #f
> (= 1 (void)) #f
> (= "apple" "APPLE") #f
> (= #:key string-upcase "apple" "APPLE") #t
> (= #:key ->number "42.0" "42/1") #t
procedure
(comparable? v) → boolean?
v : any/c
> (comparable? 3) #t
> (comparable? #\a) #t
> (comparable? "cherry") #t
> (comparable? (set)) #t
> (comparable? (hash)) #t
procedure
v : comparable?
procedure
(secondary-hash-code v) → fixnum?
v : comparable?
> (hash-code 3) 3
> (hash-code #\a) 150
> (hash-code 'abc) 17293296759617959
> (hash-code "cherry") -1027447279426916491
In order to implement this interface in custom types, all that is needed is to implement the gen:equal+hash interface. gen:comparable itself should not be implemented directly, since there is never a case where both of these interfaces would need to be implemented. To avoid any possibility of conflicting notions of equality, gen:comparable simply defers to the built-in equal? for the definition of equality for custom types.
All Racket types are gen:comparable, so = may be treated as a drop-in replacement for equal?.
1.2 Utilities
The following utilities are provided which work with any type that implements the gen:comparable interface.
procedure
key : (-> comparable? comparable?) = #f v : comparable?
> (= 1 1 1) #t
> (= 1 2) #f
> (= "apple" "apple" "apple") #t
> (= 3/2 1.5) #t
> (= #:key string-upcase "apple" "Apple" "APPLE") #t
> (= #:key ->number "42.0" "42/1" "42") #t
> (= #:key ->number "42" "42.1") #f
> (= #:key even? 12 20) #t
> (= #:key odd? 12 20) #t
> (= #:key (.. even? ->number) "12" "20") #t
procedure
key : (-> comparable? comparable?) = #f v : comparable?
procedure
key : (-> comparable? comparable?) = #f v : comparable?
procedure
key : (-> comparable? comparable?) = #f v : comparable?
> (≠ 1 1 2) #t
> (≠ 1 1) #f
> (≠ "apple" "Apple") #t
> (≠ 3/2 1.5) #f
> (≠ #:key length "cherry" "banana" "avocado") #t
procedure
key : (-> comparable? comparable?) = #f vs : (listof comparable?)
procedure
key : (-> comparable? comparable?) = #f vs : (listof comparable?)
> (group-by (list 1 2 1)) '((1 1) (2))
> (group-by (list 1 2 3)) '((1) (2) (3))
> (group-by (list 1 1 1)) '((1 1 1))
> (group-by (list 1 1 2 2 3 3 3)) '((1 1) (2 2) (3 3 3))
> (group-by (list "cherry" "banana" "apple")) '(("cherry") ("banana") ("apple"))
> (group-by #:key length (list "apple" "banana" "cherry")) '(("apple") ("banana" "cherry"))
procedure
(generic-set [#:key key] v ...) → list?
key : (-> comparable? comparable?) = #f v : comparable?
> (generic-set 1 2 3) (gset '(1 2 3) #f)
> (generic-set 1 1 2 2 3 3 3) (gset '(1 2 3) #f)
> (generic-set "cherry" "banana" "apple") (gset '("cherry" "banana" "apple") #f)
> (generic-set #:key odd? 1 2 3 4 5) (gset '(1 2) #<procedure:odd?>)
> (generic-set #:key string-upcase "apple" "Apple" "APPLE" "banana" "Banana" "cherry") (gset '("apple" "banana" "cherry") #<procedure:string-upcase>)
> (define my-set (generic-set #:key string-upcase "cherry" "banana" "apple")) > (set-add my-set "APPLE") (gset '("APPLE" "cherry" "banana") #<procedure:string-upcase>)
procedure
key : (-> comparable? comparable?) = #f elem : comparable? col : sequence?
> (tail 4 (list 1 2 3)) '()
> (tail 4 (list 1 4 3)) '(4 3)
> (->list (tail "cherry" (stream "apple" "banana" "cherry"))) '("cherry")
> (tail "BANANA" (list "apple" "banana" "cherry")) '()
> (tail #:key string-upcase "BANANA" (list "apple" "banana" "cherry")) '("banana" "cherry")
procedure
key : (-> comparable? comparable?) = #f elem : comparable? col : sequence?
> (member? 4 (list 1 2 3)) #f
> (member? 4 (list 1 4 3)) #t
> (member? "cherry" (stream "apple" "banana" "cherry")) #t
> (member? "BANANA" (list "apple" "banana" "cherry")) #f
> (member? #:key string-upcase "BANANA" (list "apple" "banana" "cherry")) #t
> (member? "BANANA" (generic-set #:key string-upcase "apple" "banana" "cherry")) #t
> (member? "tomato" (generic-set #:key length "apple" "banana" "grape")) #t