Clojure值相等性和集合

4
如果我有一个自定义类型,并使用它创建两个具有完全相同值的单独实例,那么我可以使用什么方法来确定这两个实例是等价的?`identical?`、`=`和`==`似乎都不起作用。我本来希望有一些协议来设置类型比较。最终,我希望无法将等价的内容添加到集合中。
(deftype Ref [id])
(def r1 (->Ref 1))
(def r2 (->Ref 1))
(= r1 r2) ;false rather than true
(def refs #{})
(conj refs r1 r2) ;adds both, but want one

= 可以与 defrecord 一起使用,但是如何为 deftype 定义 = 呢?


1
一个小细节:== 只适用于数字数据。user> (== :a :a) => ClassCastException clojure.lang.Keyword cannot be cast to java.lang.Number clojure.lang.Numbers.equiv (Numbers.java:206) - noisesmith
2个回答

7
在您的deftype中,扩展Object并实现equals以赋予它们相等的语义:
(deftype Ref [id]
  Object
  (equals [_ other] (= id (.id other))))

设置包含还需要哈希码支持:

(deftype Ref [id]
  Object
  (equals [_ other] (= id (.id other)))
  (hashCode [_] id) 
  clojure.lang.IHashEq 
  (hasheq [_] id))

我在这里实现了Java哈希支持和Clojure的hasheq支持。 实现IHashEq会更快。


对于 hash-set 来说,仅使用 equals 是不够的(我已经尝试过了),还需要 hashCode - noisesmith

5

defrecord已经具备了您所描述的这种行为:

user=> (defrecord Point [x y])
user.Point
user=> (= (Point. 0 0) (Point. 0 0))
true
user=> (into #{} [(Point. 0 0) (Point. 1 1) (Point. 0 0)])
#{#user.Point{:x 1, :y 1} #user.Point{:x 0, :y 0}}

deftype 另一方面默认不实现 Clojure 的结构相等性(也没有 defstruct 给我们的可读打印方法):

user=> (deftype Pair [a b])
user.Pair
user=> (= (Pair. 0 0) (Pair. 0 0))
false
user=> (into #{} [(Pair. 0 0) (Pair. 1 1) (Pair. 0 0)])
#{#<Pair user.Pair@5de3182> #<Pair user.Pair@6497d63> #<Pair user.Pair@38eed810>}

话虽如此,deftype更加强大,您可以使其按照我们的意愿进行操作:

user=> (deftype Tuple [a b]
         Object
         (equals [this other]
           (and (= (.a this) (.a other))
                (= (.b this) (.b other))))
         (toString [this]
           (str "<" (.a this) "," (.b this) ">"))
         (hashCode [this]
           (hash {:a (.a this) :b (.b this)}))
         Comparable
         (compareTo [this that]
           (compare [(.a this) (.b this)]
                    [(.a that) (.b that)])))
user.Tuple
user=> (= (Tuple. 0 0) (Tuple. 0 0))
true
user=> (into #{} [(Tuple. 0 0) (Tuple. 1 1) (Tuple. 0 0)])
#{#<Tuple <0,0>> #<Tuple <1,1>>}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接