Clojure值相等和集合

时间:2014-10-29 03:52:37

标签: clojure set

如果我有一个自定义类型并且我用它创建两个具有完全相同值的独立实例,我可以用什么方法来确定这两个东西是等价的? 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

2 个答案:

答案 0 :(得分: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会更快。

答案 1 :(得分: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>>}