2011-11-22 18 views
16

dire che vogliamo fare una funzione come minBy che restituisce tutti gli elementi di uguale minimalismo in una collezione:Tornando tipo di raccolta originale metodo generico

def multiMinBy[A, B: Ordering](xs: Traversable[A])(f: A => B) = { 
    val minVal = f(xs minBy f) 
    xs filter (f(_) == minVal) 
} 

scala> multiMinBy(List("zza","zzza","zzb","zzzb"))(_.last) 
res33: Traversable[java.lang.String] = List(zza, zzza) 

Fin qui, tutto bene, solo che abbiamo un Traversable indietro invece del nostro List iniziale.

Così ho provato a cambiare la firma per

def multiMinBy[A, B: Ordering, C <: Traversable[A]](xs: C)(f: A => B) 

nella speranza che potrei ottenere un C indietro piuttosto che un Traversable[A]. Tuttavia, non ho ricevuto nulla in cambio:

scala> multiMinBy(List("zza","zzza","zzb","zzzb"))(_.last) 

<console>:9: error: inferred type arguments [Nothing,Nothing,List[java.lang.String]] 
do not conform to method multiMinBy's type parameter bounds [A,B,C <: Traversable[A]] 

Credo che questo è perché abbiamo C che appaiono nelle argomentazioni prima di A è stato dedotto? Così ho capovolto l'ordine degli argomenti, e ha aggiunto un cast:

def multiMinBy[A, B: Ordering, C <: Traversable[A]](f: A => B)(xs: C) = { 
    val minVal = f(xs minBy f) 
    (xs filter (f(_) == minVal)).asInstanceOf[C] 
} 

che funziona, tranne che dobbiamo chiamare in questo modo:

multiMinBy((x: String) => x.last)(List("zza","zzza","zzb","zzzb")) 

c'è un modo per mantenere la sintassi originale, ottenendo il giusto tipo di collezione?

risposta

20

Penso che la soluzione di Miles Sabin sia troppo complessa. La collezione di Scala ha già i macchinari necessari per farlo funzionare, con una modifica molto piccola:

import scala.collection.TraversableLike 
def multiMinBy[A, B: Ordering, C <: Traversable[A]] 
       (xs: C with TraversableLike[A, C]) 
       (f: A => B): C = { 
    val minVal = f(xs minBy f) 
    xs filter (f(_) == minVal) 
} 
+3

Sì, sono d'accordo, questa è una soluzione migliore della mia. –

10

Il tuo problema è che se visto come un metodo su GenTraversable[A] (che userò invece di Traversable[A] in questa risposta) il tipo di risultato del metodo filter non è più preciso di GenTraversable[A]. Sfortunatamente all'interno del corpo del metodo multiMinBy come scritto è tutto ciò che sai su xs.

Per ottenere il risultato desiderato, è necessario rendere più precisa la firma di multiMinBy. Un modo per farlo pur lasciando il tipo di contenitore relativamente aperto è di usare un tipo strutturale come segue,

type HomFilter[CC[X] <: GenTraversable[X], A] = 
    CC[A] { def filter(p : A => Boolean) : CC[A] } 

def multiMinBy[CC[X] <: GenTraversable[X], A, B: Ordering] 
    (xs: HomFilter[CC, A])(f: A => B) : CC[A] = { 
    val minVal = f(xs minBy f) 
    xs filter (f(_) == minVal) 
    } 

Il tipo strutturale HomFilter ci permette di affermare che l'argomento multiMinBy deve avere un metodo filter con la desiderata tipo di risultato.

sessione REPL

campione,

scala> val mmb = multiMinBy(List("zza","zzza","zzb","zzzb"))(_.last) 
mmb: List[String] = List(zza, zzza) 

Tenete a mente che questo è un requisito più rigoroso rispetto a quello del contenitore solo essere Traversable: è ammissibile per i sottotipi di GenTraversable per definire filter metodi che non sono regolari in questo modo . La firma sopra impedirà staticamente che i valori di tali tipi vengano passati a multiMinBy ... presumibilmente questo è il comportamento che stai cercando.

+0

Si può evitare l'intera tipizzazione strutturale semplicemente usando 'TraversableLike'. –

13

Che ne dici di utilizzare CanBuildFrom?

import scala.collection.immutable._ 
import scala.collection.generic._ 

def multiMinBy[A, B, From[X] <: Traversable[X], To](xs: From[A])(f: A => B) 
    (implicit ord: Ordering[B], bf: CanBuildFrom[From[_], A, To]) = { 
    val minVal = f(xs minBy f) 
    val b = bf() 
    b ++= (xs filter (f(_) == minVal)) 
    b.result 
} 



scala> multiMinBy(List("zza","zzza","zzb","zzzb"))(_.last) 
res1: List[java.lang.String] = List(zza, zzza) 
Problemi correlati