2011-10-06 19 views
5

Niedawno poprosił o klucze kompozytowych w mapach w Clojure: How can you implement Composite keys in clojure? ...Co to jest odpowiednik clojure Overriding "equals" w java?

Odpowiedzią było to, że działają one podobnie do kluczy java - jeśli coś nadpisuje „równa się”, to może być skutecznie stosowany jako klucz.

Zastanawiam się: czy istnieją makra, które pozwalają nam przesłonić "równe" dla niestandardowych struktur danych? Powiedzmy na przykład, że chcę użyć mapy jako klucza i zdefiniować unikalność jako "jeśli ta mapa zawiera co najmniej dwa elementy wspólne z inną mapą, są one równe". Jak mogę przesłonić domyślne zachowanie mapy?

W java, uważam ten obiekt za dość potężny przy tworzeniu map o dużej prędkości z tysiącami fasoli jako kluczami.

Odpowiedz

4

To nie jest tak, że kolekcje clojure zastępują jest równy; nadpisywanie równych pozycji zdarza się niemal wszędzie, gdy robisz coś interesującego. Typy Clojure zapewniają implementację równań, która ma być spójna dla całego języka (prawdopodobnie w taki sposób, że Java ma być używana). Oznacza to, że rzeczy, które są "równe", powinny być pojedynczymi przedmiotami w zestawach i pojedynczymi kluczami na mapach, zawsze, wszędzie. Projekt języka zależy od tego. Clojure 1.3 wprowadził pewne wyraźne, niezgodne z poprzednimi wersjami zmiany, aby zbliżyć się do tego ideału.

Odejście od tego oczekiwanego zachowania równych jest bardzo prawdopodobne, w jakiś sposób spowoduje problemy. Nie jest też trudno używać własnych, podobnych do zestawu kompozytów, kiedy naprawdę ich potrzebujesz, nie zmuszając do tego swojej woli na równych prawach.

Powiedziawszy, możesz użyć wielu funkcji java interop i makr, aby wypaczyć system równań, jeśli naprawdę chcesz. Zobacz http://clojure.org/datatypes jako punkt wyjścia.

1

To szaleństwo zarówno w Clojure, jak i Java. Nie rób tego. Będziesz łamał kontrakt klasy Object i interfejs Map; wszystkie rodzaje rzeczy całkowicie się rozpadną, jeśli użyją twojej niestandardowej klasy mapy. Jeśli stwierdzisz, że dwa obiekty to equal, to ich kody hash muszą być identyczne, inaczej trzymanie ich przez HashMap nie będzie w stanie sobie poradzić. Oczywiście, wszystko można oczywiście zaimplementować do 0, ale tam jest twoja mapa o wysokiej wydajności - jest teraz połączoną listą.

Powiedziawszy, możesz zrobić coś takiego w Clojure lub Java, używając odpowiednio sorted-map (lub SortedMap) z niestandardowym komparatorem. Istnieją jednak miliony pułapek, jeśli wybierzesz Komparator, który nie realizuje równości, ale zamiast tego pojawia się rodzaj "rozmytej równości".

+0

Przesłanianie równań jest w porządku, o ile tylko przesłonisz funkcję mieszania.Nie szaleństwo - standardowy, dobry dobry projekt. Każdy dobry program typu GUI/lint-type będzie przypominał o zastąpieniu jednego, jeśli zastąpisz drugi. Używanie komparatora jest nieco gorszym schematem, ponieważ eksportuje wiedzę z twojej klasy do innej (komparatora). –

+2

To wygląda na zderzenie paradygmatu. Niektórzy myślą "samodzielne obiekty, pytają, czy są równi", podczas gdy inni myślą o "składzie niezmiennych wartości", gdzie idea równości jest uniwersalna. –

+3

Nie jestem przeciwnikiem przeciążania - to oczywiste, często konieczne w Javie. Zastąpienie go * w sposobie, w jaki pyta * jest okropnym pomysłem, ponieważ niemożliwe jest napisanie funkcji 'hashCode', która jest zgodna z decyzją, aby' równy' zwracał wartość true dla każdej mapy, która ma co najmniej dwa klucze z tym . – amalloy

5

Aby dodać trochę jasności do tej dyskusji, w języku Java często zastępuje się hashCode i equals. Na przykład próbujesz śledzić pracowników, którzy mają imiona, ale mogą również mieć pseudonim. Celem jest upewnienie się, że pracownicy nie są duplikowani. W takim przypadku przesłonię equals i hashCode, aby przejrzeć tylko identyfikator pracownika. Kiedy ci pracownicy są wpisani w strukturę danych zestawu, nie są duplikowani. W Clojure, można zrobić:

(deftype Employee [name id] 
    Object 
    (equals [a b] (= (.id a) (.id b))) 
    (hashCode [this] (.hashCode (.id this))) 
    (toString [this] (.name this))) 

(def vince (Employee. "Vince" 42)) 

(def vincent (Employee. "Vincent" 42)) 

(def tony (Employee. "Tony" 2)) 

(into #{} [vince vincent tony]) 

Ale może chcesz kontynuować „czystego Clojure” rozwiązanie struktury danych aniżeli dzieje się hashcode, równa, Java współdziałanie drogowego.

+0

Jak wyglądałoby rozwiązanie struktury danych "czystego Clojure"? –

Powiązane problemy