2012-06-19 14 views
13

Dadas dos tuplas de la misma aridad, ¿cómo puedo compararlas lexicográficamente? Parece que esto debería ser tan simple como en el siguiente fragmento, pero no lo es. ¿Algún ejemplo simple de cómo hacerlo?¿Cómo comparar lexicográficamente las scala tuples?

var x = (1,2,3) < (1,2,4) 

¿Se han enumera, podría definir una función recursiva que compare la cabeza de las listas hasta que se encontró una diferencia o al final de una lista, pero no creo que pudiera hacer eso por tuplas.

Respuesta

22

No es sencillo porque mientras

var x = (1,2,3) < (1,2) 

parece bastante simple,

var x = (1,false,3) < (1,2) 

no lo es. ¿Cómo manejas los tipos no ordenados? ¿Cómo manejas diferentes tipos en la misma posición de tupla?

¿Usted exige que todos los tipos sean iguales? En ese caso, no tienes una tupla. El objetivo de una tupla es que su aridad sea fija (usted sabe estáticamente lo grande que es) y cada elemento puede ser de un tipo diferente.

Si me encontré con ese problema, y ​​me esforzaría mucho para no hacerlo, tomaría Shapeless, convertiría las tuplas en algo así como HLists, y luego trataría de comparar eso.

EDITAR

Ah, ahora es mucho más fácil:

import scala.math.Ordering.Implicits._ 
var x = (1,2,3) < (1,2,4) 

Estos implícitos adicionales que no están disponibles de forma automática, ya que pueden dar lugar a que diverge implícitos en algunas circunstancias.

+0

Buenos puntos, Daniel. He editado mi pregunta para indicar que las tuplas comparadas siempre tienen el mismo valor. En cuanto a los tipos no ordenados, mis tuplas deberían tener símbolos (esa es la razón por la que recurrí a las tuplas en lugar de a las listas), pero como no he encontrado ninguna forma de ordenar símbolos, utilizaré los números. ¿Debo usar listas entonces? ¿Qué pasa si realmente necesito agregar símbolos a la lista? – lasaro

+1

@lasaro Bueno, ahora que simplificó el problema, hay una solución relativamente fácil. Ver respuesta editada. –

+0

Gracias Daniel, por tu ejemplo muy conciso. – lasaro

2

La manera más fácil es definir un orden implícito [T] en ellos, pero luego debe pasar este orden a la función de ordenación (o cualquier otra función que quiera compararlos). También es posible pasarlo implícitamente.

Otra forma sería, para extender la clase tupla por el operador < a través de una conversión implícita:

implicit def compareTuple[T](lhs: (T,T)) = new { 
    def <(rhs: (T,T)) = lhs._1<rhs._1 || (lhs._1==rhs._1 && lhs._2<rhs._2) 
} 

edición: Si usted quiere tener los otros operadores de comparación también se podía conseguir heredando de ordenada [T]:

implicit def compareTuple[T](lhs: (T,T)) = new Ordered[(T,T)] { 
    def compare(rhs: (T,T)) = ... 
} 

Edit2: Si también necesita comparar tuplas de diferentes tamaños, puede usar la función productIterator que se define en todas las clases de tuplas (see documentation) y le permite obtener un iterador sobre la tupla. De esta forma podrías escribir una función como la que harías con una lista.

Edit3: Esto sería algo así como:

implicit def compareTuple[T <: Product](lhs: T) = new Ordered[T] { 
    def compare[U <: Product](rhs: U) = { 
     def compare(lhs: Any, rhs: Any) = ... 
     def iteratorCompare(lhs: Iterator[Any], rhs: Iterator[Any]):Int = 
      if(!lhs.hasNext) 
       if(!rhs.hasNext) 
        0 
       else 
        -1 
      else if(!rhs.hasNext) 
       1 
      else 
       compare(lhs.next,rhs.next) 
     iteratorCompare(lhs.productIterator,rhs.productIterator) 
    } 
} 

Pero con este enfoque tiene que tener cuidado acerca de los tipos. Como la función no conoce los tipos de los elementos de tupla (pueden ser diferentes dentro de la misma tupla), solo puede proporcionarle un iterador [Cualquiera]. Por lo tanto, debe definir una función de comparación (Cualquiera, Cualquiera) para manejar lo que desee.

+1

Posiblemente pueda habilitar '-Xexperimental' que elegirá LUB en lugar de' Any'. – soc

+0

Gracias Heinzi, por su respuesta completa. Aprendí mucho de eso, pero por el nivel de mis habilidades de preguntas y scala, el de Daniel fue más directo. – lasaro

9

La solución de Daniel funciona si desea usar < pero si necesita un método compare puede hacer lo siguiente (por ejemplo).

implicitly[Ordering[Tuple2[Int, Int]]].compare((1,2), (2,3)) 

Hay pedidos definidos para todas las tuplas con piezas comparables.

Cuestiones relacionadas