2012-02-10 27 views
6

Znalazłem zachowanie Clojure mylące dotyczące równości map i zapisów. W tym pierwszym przykładzie mamy dwa różne typy, które są strukturalnie równe. Równość = funkcja zwraca true:Mapy i rekordy równości w Clojure

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 

W drugim przykładzie mamy HashMap i rekord, które są strukturalnie równoważne ale funkcja = zwraca fałsz:

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 

Dlaczego są różnice? Używam Clojure 1.3 i uważam, że są naprawdę mylące.

Odpowiedz

14

Z docstring dla defrecord:

Ponadto defrecord zdefiniuje = rodzaj i-wartości oparte, a zdefiniowane Java .hashCode i .equals zgodnego z umową przez java.util .Mapa.

Tak więc, przy użyciu =, jest brany pod uwagę typ. Można użyć .equals zamiast:

user> (.equals titi {:a 1 :b 2}) 
true 
+0

Dlaczego wystąpienia właściwości PersistentArrayMap i PersistentHashMap są równe =, ponieważ funkcja typu wskazuje, że nie są one tego samego typu? – z1naOK9nu8iY5A

+7

Obietnica "typ i wartość =" znajduje się w dokumencie "defrecord" i dotyczy rekordów. Z drugiej strony, zwykłe mapy mają uczestniczyć w schemacie opartym na wartości i tak, że "(= (hash-map: foo 1: bar 2) (posortowana-mapa: foo 1: pasek 2)) 'i' (= (java.util.HashMap. {: foo 1: bar 2}) {: foo 1: bar 2}) 'są obie" prawdziwe ". –

8

PersistentArrayMap i PersistentHashMap są koncepcyjnie takie same - jak rośnie ArrayMap, zostanie ona automatycznie przekonwertowane do HashMap ze względu na wydajność. Kod na poziomie użytkownika zasadniczo nie powinien próbować rozróżniać tych dwóch.

Z drugiej strony, typ danych defrecord nie jest taki sam jak jedna z pozostałych. Jest to osobny typ, który może implementować całkowicie różne interfejsy i nie powinien być automatycznie zastępowany przez inną formę mapy. Nie jest to koncepcyjnie równa normalnej mapie, więc = zwraca wartość false.