2010-02-26 20 views
8

Acabo de tropezar con uno de Tony Morris 'blog-posts about Java y un problema fundamental con el lenguaje: el de definir una relación de igualdad a medida para una colección. Esto es algo que creo que es un gran problema y me preguntaba si había alguna solución scala.Relaciones de igualdad en Scala

El problema clásico se manifiesta al pensar, por ejemplo, en una operación. Digamos que hago dos intercambios de +100 participaciones de vodafone a 150p. Los dos intercambios son iguales, ¿sí? Excepto que no son la misma operación. En el caso de un sistema normal del mundo real, con persistencia o serialización, no puedo confiar en identidad para decirme si dos referencias son la misma operación!

Así que lo que quiero es ser capaz de crear una colección que me puede pasar una igualdad-relación con:

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

¿Cómo puedo aplicar mi sistema de una manera eficiente (a menos que el EqualityRelation define también una hash mecanismo)?

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

Así que las preguntas son:

  • ¿Hay una biblioteca que proporciona esta capacidad?
  • ¿Hay alguna forma de hacerlo bien en Scala?

Parece que con implícitos, que sería una cosa bastante fácil de añadir a la Scala Set tipo existente.

+0

Equal in Scalaz: http://github.com/scalaz/scalaz/blob/master/example/src/main/scala/scalaz/ExampleEqual.scala. Pero no estoy lo suficientemente familiar como para decir qué se basa en eso. –

+0

Creo que es solo un tipo seguro, por lo que '" Hola "=== 2' no compila –

+0

scalaz.Equal no es solo tipo seguro, también es flexible. 'Equal [List [Foo]]]' es parametrizable por un 'Equal [Foo]'. Esto va a mitad de camino hacia tu objetivo. Martin Odersky declinó agregar 'Hash [T]' a la biblioteca estándar, diciendo que "queremos mantener el hashing universal, es demasiado parte de la cultura Java". http://www.scala-lang.org/node/4091#comment-16327 – retronym

Respuesta

6

Esto ya se puede conseguir con TreeSet de Java y una aplicación Comparador:

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); 

Salida:

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

Esto tiene las desventajas de que el Comparador de implementar es más potente de lo necesario y que usted está restringido a colecciones que admiten comparadores. Como lo señala oxbow_lakes, la implementación de Comparator rompe el contrato de configuración (para !a.equals(b) podría ser new Set(); set.add(a) == true && set.add(b) == false).

Scala admite esto con una transformación de vista desde A => Ordenado [A].

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

Y se ve forzado a seguir la ruta del tiempo de acceso O (log (n)) de una estructura de árbol –

+0

También, desde el JavaDoc de ' TreeSet': * Tenga en cuenta que la ordenación mantenida por un conjunto (independientemente de que se proporcione o no un comparador explícito) debe ser coherente con igual si se quiere implementar correctamente la interfaz Set *, por lo que parece que mientras este enfoque funciona, no funciona Creo que me siento bien –

+0

Este es un punto válido. Romper el contrato establecido es una mala idea. Si le das este TreeSet, tiene que ser envuelto. El CleverSet que sugirió lo rompería de la misma manera. Solo podría implementar Iterable [T] not Set [T]. –

2

Usted está describiendo el concepto de una estrategia de hash. El Trove library incluye conjuntos y mapas que se pueden construir con estrategias hash.

+0

Gracias por el enlace –

2

Sé que estás preguntando por Scala, pero vale la pena compararlo con lo que ofrecen las colecciones de .Net. En particular, todas las colecciones basadas en Hash (por ejemplo, Dictionary<TKey, TValue> y HashSet<T>) pueden tomar una instancia de IEqualityComparer<T>. Esto es similar al Equiv[T] de Scala, pero también proporciona un código hash personalizado.Se puede crear un rasgo similares subclasificando Equiv:

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

ser apoyado totalmente, colecciones basadas picadillo tendrían que añadir HashEquiv parámetros implícitos a su construcción y el uso de las implícita importados métodos equiv y hashOf en lugar de los métodos de instancia Object (como TreeSet, etc. hacer con el rasgo Ordered, pero en reversa). También debería haber una conversión implícita de Any a HashEquiv que utiliza la implementación intrínseca equals y hashCode.