2012-06-19 14 views
13

Dato due tuple della stessa unità, come posso confrontarle lessicograficamente? Sembra che questo dovrebbe essere semplice come nel seguente frammento, ma non lo è. Qualche semplice esempio di come farlo?Come confrontare lessicograficamente scala tuple?

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

Erano elenca, potrei definire una funzione ricorsiva che avrebbe confrontare testa delle liste fino a quando è stato trovato una differenza o la fine di una lista, ma non credo che potrei farlo per tuple.

risposta

22

Non è semplice perché mentre

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

sembra piuttosto semplice,

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

non è. Come gestisci i tipi non ordinati? Come gestisci tipi diversi nella stessa posizione di tupla?

Obbliga tutti i tipi a essere lo stesso? In tal caso, non hai una tupla. L'intero punto di una tupla è che la sua arità è fissa (tu sai staticamente quanto è grande) e ogni elemento può essere di un tipo diverso.

Se mi sono trovato con quel problema - e mi piacerebbe provare molto difficile non - mi piacerebbe afferrare informe, convertire le tuple in qualcosa come HLists, e quindi provare a confrontare su questo.

EDIT

Ah, ora è molto più facile:

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

Questi impliciti supplementari non sono disponibili automaticamente, perché possono causare divergenza impliciti in alcune circostanze.

+0

Buoni punti, Daniel. Ho modificato la mia domanda per dichiarare che le tuple hanno sempre la stessa priorità. Per quanto riguarda i tipi non ordinati, le mie tuple dovrebbero avere simboli (questa è la ragione per cui ho scelto le tuple anziché le liste), ma poiché non ho trovato alcun modo di ordinare simboli, userò, invece, numeri. Dovrei usare le liste allora? Cosa succede se ho davvero bisogno di aggiungere simboli alla lista? – lasaro

+1

@lasaro Bene, ora che hai semplificato il problema, c'è una soluzione relativamente facile. Vedi la risposta modificata. –

+0

Grazie Daniel, per il tuo esempio molto conciso. – lasaro

2

Il modo più semplice è definire un Ordine implicito [T] su di essi, ma in seguito è necessario passare questo ordine alla funzione di ordinamento (o qualsiasi altra funzione che desideri confrontarli). È anche possibile passarlo implicitamente.

Un altro modo sarebbe, per estendere la classe tupla dall'operatore < tramite un cast implicito:

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

edit: Se si desidera avere gli altri operatori di confronto troppo, si potrebbe ottenere loro ereditando dalla ordinato [T]:

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

EDIT2: Se hai bisogno anche di confrontare tuple di diverse dimensioni, è possibile utilizzare la funzione productIterator definita in tutte le classi di tuple (see documentation) e consente di ottenere un iteratore sulla tupla. In questo modo potresti scrivere una funzione come faresti con una lista.

Edit3: Questo sarebbe qualcosa di simile:

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

Ma con questo approccio si deve prendere cura sui tipi. Poiché la funzione non conosce i tipi di elementi della tupla (possono essere diversi all'interno della stessa tupla), può solo fornirti un Iterator [Qualsiasi]. Quindi devi definire una funzione di confronto (Qualsiasi, Qualsiasi) per gestire ciò che desideri.

+1

È possibile abilitare '-Xexperimental', che sceglierà LUB anziché' Any'. – soc

+0

Grazie Heinzi, per la tua risposta completa. Ho imparato molto da esso, ma per il livello della mia domanda e le abilità di scala, Daniel era più al punto. – lasaro

9

La soluzione di Daniel funziona se si desidera utilizzare < ma se è necessario un metodo compare è possibile eseguire quanto segue (ad esempio).

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

Sono disponibili ordini per tutte le tuple con parti comparabili.

Problemi correlati