Равенство карт и записей в Clojure

Я обнаружил, что поведение Clojure сбивает с толку в отношении равенства между картами и записями. В этом первом примере у нас есть два разных типа, которые структурно одинаковы. Функция равенства = возвращает true:

user> (defn make-one-map
         []
       {:a "a" :b "b"})
#'user/make-one-map
user> (def m1 (make-one-map))
#'user/m1
user> m1
{:a "a", :b "b"}
user> (def m2 {:a "a" :b "b"})
#'user/m2
user> m2
{:a "a", :b "b"}
user> (= m1 m2)
true
user> (type m1)
clojure.lang.PersistentArrayMap
user> (type m2)
clojure.lang.PersistentHashMap

Во втором примере у нас есть хэш-карта и запись, которые структурно эквивалентны, но функция = возвращает false:

user> (defrecord Titi [a b])
user.Titi
user> (def titi (Titi. 1 2))
#'user/titi
user> titi
#user.Titi{:a 1, :b 2}
user> (= titi {:a 1 :b 2})
false

Почему существуют различия? Я использую Clojure 1.3, и я нашел их очень запутанными.


person z1naOK9nu8iY5A    schedule 10.02.2012    source источник


Ответы (2)


Из строки документации для defrecord:

Кроме того, defrecord определяет = на основе типа и значения и определяет Java .hashCode и .equals в соответствии с контрактом для java.util.Map.

Итак, при использовании = учитывается тип. Вместо этого вы можете использовать .equals:

user> (.equals titi {:a 1 :b 2})
true
person Justin Kramer    schedule 10.02.2012
comment
Почему экземпляры PersistentArrayMap и PersistentHashMap равны =, тогда как функция типа указывает, что они не одного типа? - person z1naOK9nu8iY5A; 10.02.2012
comment
Обещание = на основе типа и значения указано в строке документации defrecord и применяется к записям. Обычные карты, с другой стороны, должны участвовать в схеме, основанной на значении =, и они это делают до такой степени, что (= (hash-map :foo 1 :bar 2) (sorted-map :foo 1 :bar 2)) и (= (java.util.HashMap. {:foo 1 :bar 2}) {:foo 1 :bar 2}) оба являются true. - person Michał Marczyk; 11.02.2012

PersistentArrayMap и PersistentHashMap концептуально одинаковы — по мере роста ArrayMap он автоматически преобразуется в HashMap по соображениям производительности. Код пользовательского уровня, как правило, не должен пытаться различать их.

С другой стороны, тип данных defrecord не совпадает с одной из других карт. Это отдельный тип, который может реализовывать совершенно разные интерфейсы и не должен автоматически заменяться какой-либо другой формой карты. Концептуально она не равна карте нормалей, поэтому = возвращает false.

person Retief    schedule 10.02.2012