2009-12-08 16 views
7

In Scala, mi piacerebbe essere in grado di scrivere classi generiche che usano operatori come>, /, * ecc, ma non vedo come vincolare T in modo che funzioni.Scala: Qual è il modo migliore per eseguire operazioni numeriche in classi generiche?

Ho cercato T vincolato con Ordinato [T], ma non sembra funzionare poiché solo RichXXX (ad esempio RichInt) lo estende, non Int ecc. Ho visto anche Numeric [T], è disponibile solo in Scala 2.8?

Ecco un esempio specifico:

class MaxOfList[T](list: List[T]) { 
    def max = { 
    val seed: Option[T] = None 

    list 
     .map(t => Some(t)) 
     // Get the max  
     .foldLeft(seed)((i,m) => getMax(i,m)) 
    } 

    private def getMax(x: Option[T], y: Option[T]) = { 
    if (x.isDefined && y.isDefined) 
     if (x > y) x else y 
    else if (x.isDefined) 
     x 
    else 
     y 
    } 
} 

Questa classe non compilerà, perché ci sono molti Ts che non supportano> ecc

Pensieri?

Per ora ho usato un tratto MixIn per aggirare il problema:

/** Defines a trait that can get the max of two generic values 
*/ 
trait MaxFunction[T] { 
    def getMax(x:T, y:T): T 
} 

/** An implementation of MaxFunction for Int 
*/ 
trait IntMaxFunction extends MaxFunction[Int] { 
    def getMax(x: Int, y: Int) = x.max(y) 
} 

/** An implementation of MaxFunction for Double 
*/ 
trait DoubleMaxFunction extends MaxFunction[Double] { 
    def getMax(x: Double, y: Double) = x.max(y) 
} 

Che se alteriamo la classe originale può essere miscelato in al momento dell'istanza.

P.S. Mitch, ispirato dal ri-scrittura di getMax, qui è un altro:

private def getMax(xOption: Option[T], yOption: Option[T]): Option[T] = (xOption,yOption) match { 
    case (Some(x),Some(y)) => if (x > y) xOption else yOption 
    case (Some(x), _) => xOption 
    case _ => yOption 
    } 

risposta

9

È possibile utilizzare View Bounds.

In breve, def foo[T <% U](t: T) è una funzione che prenderà qualsiasi T che sia o possa essere convertita implicitamente in un U. Poiché un Int può essere convertito in un RichInt (che contiene il metodo desiderato), questo è un esempio eccellente di utilizzo.

class MaxOfList[T <% Ordered[T]](list: List[T]) { 
    def max = { 
    val seed: Option[T] = None 
    list.foldLeft(seed)(getMax(_,_)) 
    } 

    private def getMax(xOption: Option[T], y: T) = (xOption, y) match { 
    case (Some(x), y) if (x > y) => xOption 
    case (_, y) => Some(y) 
    } 
} 

PS - Ho riscritto il metodo getMax (...) per confrontare i valori al posto delle opzioni stesse, e usato il pattern matching, invece di TestDefinito (...)

PPS - Scala 2.8 sarà avere un tratto numerico che potrebbe essere utile. http://article.gmane.org/gmane.comp.lang.scala/16608


Addendum

Solo per risatine, ecco la versione super-compatta che elimina il metodo getMax del tutto:

class MaxOfList[T <% Ordered[T]](list: List[T]) { 
    def max = list.foldLeft(None: Option[T]) { 
     case (Some(x), y) if (x > y) => Some(x) 
     case (_, y) => Some(y) 
    } 
} 

Yet Another Addendum

Questa versione sarebbe più efficiente per le grandi liste ... evita la creazione di alcuni (x) per ogni elemento:

class MaxOfList[T <% Ordered[T]](list: List[T]) { 
    def max = { 
    if (list.isEmpty) None 
    else Some(list.reduceLeft((a,b) => if (a > b) a else b)) 
    } 
} 

Last One, lo prometto!

A questo punto, si può semplicemente abbandonare la classe e utilizzare una funzione:

def max[T <% Ordered[T]](i: Iterable[T]) = { 
    if (i.isEmpty) None 
    else Some(i.reduceLeft((a,b) => if (a > b) a else b)) 
    } 
+0

Grazie ancora Mitch. Giuro che ci ho provato e non ho potuto farlo compilare! –

+0

Ho aggiunto un aggiornamento alla domanda originale con un'altra versione di getMax ispirata alla tua. –

+0

Ho aggiornato il mio getMax con un'altra versione ispirata alla tua. Quante altre iterazioni prima di raggiungere una sorta di singolarità? –

2

Con Numeric[T] in Scala 2.8,

scala> case class MaxOfList[T : Numeric](list: List[T]) { 
    |  def max = if(list.isEmpty) None else Some(list.max) 
    | } 
defined class MaxOfList 

scala> MaxOfList(1::2::3::9::7::Nil).max 

res1: Option[Int] = Some(9) 

scala> MaxOfList(1.5::3.9::9.2::4.5::Nil).max 

res2: Option[Double] = Some(9.2) 

scala> MaxOfList(Nil: List[Byte]).max 

res3: Option[Byte] = None 

scala> 
1

Come risposto Mitch Blevins, questo è quello vista limiti sono stati fatti per. Ora, mentre Numeric è qualcosa che viene aggiunto solo su Scala 2.8, non che non dipenda da alcuna funzione di Scala 2.8. Uno potrebbe fare la stessa cosa su Scala 2.7.

Si può essere interessati in alcuni frammenti di codice generici che ho scritto per Rosetta Code:

Problemi correlati