2010-02-26 13 views
8

Po prostu natknąłem się na jeden z Tony'ego Morrisa: "blog-posts about Java" i podstawowy problem z językiem: definiowanie dostosowanej relacji z równością dla kolekcji. Jest to coś, co myślę, że jest to wielka sprawa i zastanawiałem się, czy było jakieś rozwiązanie scala.Relacje równości w Scali

Klasyczna kwestia przejawia się w myśleniu o, powiedzmy, handlu. Powiedzmy, że wykonuję dwie transakcje +100 udziałów vodafone @ 150p. Te dwie transakcje są równe, tak? Z tym wyjątkiem, że nie są to o tej samej nazwie handlowej. W przypadku normalnego, rzeczywistego systemu, z utrwalaniem się lub serializacją, nie mogę polegać na tożsamości, aby powiedzieć mi, czy dwa odnośniki są do tym samym handlem!

Więc czego chcę to, aby móc stworzyć kolekcję, które mogę przekazać równouprawnienia odniesieniu do:

val as = CleverSet[Trade](IdEquality) 
val bs = CleverSet[Trade](EconomicsEquality) 

Jak wdrożyć mój zestaw w sposób efektywny (chyba że EqualityRelation określa również hash mechanizm)?

trait EqualityRelation[T] { 
    def equal(t1: T, t2: T) : Boolean 
    def hash(t: T) : Int 
} 

więc pytania są:

  • Czy istnieje biblioteka, która oferuje tę możliwość?
  • Czy jest jakiś sposób robienia tego zgrabnie w Scali?

Wygląda na to, że dodanie impulsu do istniejącego typu scala Set wydaje się dość łatwe.

+0

Jest równy w Scalaz: http://github.com/scalaz/scalaz/blob/master/example/src/main/scala/scalaz/ExampleEqual.scala. Ale nie jestem dość znany, aby powiedzieć, co na nim opiera. –

+0

Myślę, że to po prostu "safe" jest równy, więc '' Hello "=== 2' nie kompiluje –

+0

scalaz.Equal jest nie tylko bezpiecznym typem, ale także elastycznym. 'Equal [List [Foo]]]' można parametryzować za pomocą 'Equal [Foo]'. To idzie w połowie do celu. Martin Odersky odmówił dodania "Hash [T]" do standardowej biblioteki, mówiąc, że "chcemy utrzymać uniwersalne hashu, to zbyt duża część kultury Java". http://www.scala-lang.org/node/4091#comment-16327 – retronym

Odpowiedz

6

ten można już osiągnąć Java TreeSet i realizacji komparatora:

TreeSet<String> ignoreCase = new TreeSet<String>(new Comparator<String>(){ 
    @Override 
    public int compare(String o1, String o2) { 
     return o1.compareToIgnoreCase(o2); 
    }}); 

TreeSet<String> withCase = new TreeSet<String>(); 

List<String> values = asList("A", "a"); 
ignoreCase.addAll(values); 
withCase.addAll(values); 

wyjściowa:

ignoreCase -> [A] 
withCase -> [A, a] 

Ma wady, że komparator do wdrożenia jest silniejsze niż to konieczne i że "ogranicza się do zbiorów, które obsługują komparatory. Jak wskazano przez oxbow_lakes, implementacja Komparatora rozbija umowę Set (dla !a.equals(b) może to być new Set(); set.add(a) == true && set.add(b) == false).

Scala obsługuje to z transformacją widoku z A => Zamówione [A].

scala> new scala.collection.immutable.TreeSet[String]()(x=> x.toLowerCase) + "a" 
+ "A" 
res0: scala.collection.immutable.TreeSet[String] = Set(A) 
+1

I jesteś zmuszony do pokonania trasy czasu dostępu do O (log (n)) struktury drzewa –

+0

Również z JavaDoc z ' TreeSet': * Zauważ, że porządek utrzymywany przez zestaw (niezależnie od tego, czy zapewniony jest wyraźny komparator) musi być zgodny z równymi, jeśli ma poprawnie implementować interfejs Set *, więc wydaje się, że podczas gdy to podejście działa, to doesn ' t całkiem czuję się dobrze –

+0

To jest poprawny punkt. Zerwanie umowy z Setem to zły pomysł. Jeśli wręczysz to TreeSet dookoła, musi on być owinięty. Sugerowany CleverSet złamie go w ten sam sposób. Mogłaby tylko wprowadzić Iterable [T] not Set [T]. –

2

Opisujesz pojęcie strategii hashowania. Model Trove library zawiera zestawy i mapy, które można utworzyć za pomocą strategii mieszania.

+0

Dzięki za link –

2

Wiem, że pytasz o Scalę, ale warto porównać to z ofertą kolekcji .Net. W szczególności wszystkie kolekcje oparte na haszy (np. Dictionary<TKey, TValue> i HashSet<T>) mogą przyjmować instancję IEqualityComparer<T>. Jest to podobne do Scala z numerem Equiv[T], ale zapewnia także niestandardowy kod skrótu.Można utworzyć podobną cechę przez instacji Equiv:

trait HashEquiv[T] extends Equiv[T] { 
    def hashOf(t: T) : Int 
} 

Aby być w pełni obsługiwany, zbiory hash oparty musiałby dodać HashEquiv ukryte parametry do ich budowy i używać niejawnie importowane equiv i hashOf metod zamiast z Object metody instancji (jak TreeSet, itp. z cechą Ordered, ale w odwrotnej kolejności). Konieczne byłoby również niejawne przekształcenie z Any na HashEquiv, które wykorzystuje wewnętrzną implementację equals i hashCode.