2012-03-15 14 views
5

Vorrei scrivere un metodo mergeKeys che raggruppa i valori in un Iterable[(K, V)] mediante le chiavi. Per esempio, ho potuto scrivere:Valori di gruppo con una chiave con qualsiasi Monoid

def mergeKeysList[K, V](iter: Iterable[(K, V)]) = { 
    iter.foldLeft(Map[K, List[V]]().withDefaultValue(List.empty[V])) { 
     case (map, (k, v)) => 
      map + (k -> (v :: map(k))) 
    } 
    } 

Tuttavia, mi piacerebbe essere in grado di utilizzare qualsiasi Monoid invece di scrivere un metodo per List. Ad esempio, i valori possono essere numeri interi e voglio sommarli anziché aggiungerli in un elenco. Oppure potrebbero essere delle tuple (String, Int) dove voglio accumulare le stringhe in un set ma aggiungere gli interi. Come posso scrivere un tale metodo? O c'è qualcos'altro che posso usare in scalaz per fare questo?

Aggiornamento: non ero così lontano come pensavo. Mi sono avvicinato un po ', ma non so ancora come farlo funzionare se i valori sono tuple. Devo scrivere ancora un'altra conversione implicita? Vale a dire una conversione implicita per ogni numero di parametri del tipo?

sealed trait SuperTraversable[T, U, F[_]] 
extends scalaz.PimpedType[TraversableOnce[(T, F[U])]] { 
    def mergeKeys(implicit mon: Monoid[F[U]]): Map[T, F[U]] = { 
    value.foldLeft(Map[T, F[U]]().withDefaultValue(mon.zero)) { 
     case (map, (k, v)) => 
     map + (k -> (map(k) |+| v)) 
    } 
    } 
} 

implicit def superTraversable[T, U, F[_]](
    as: TraversableOnce[(T, F[U])] 
): SuperTraversable[T, U, F] = 
    new SuperTraversable[T, U, F] { 
     val value = as 
    } 

risposta

6

In primo luogo, mentre non è rilevante per la tua domanda, si stanno limitando generalità del vostro codice menzionando esplicitamente il tipo di costruzione F[_]. Funziona bene senza farlo:

sealed trait SuperTraversable[K, V] 
extends scalaz.PimpedType[TraversableOnce[(K, V)]] { 
    def mergeKeys(implicit mon: Monoid[V]): Map[K, V] = { 
     value.foldLeft(Map[K, V]().withDefaultValue(mon.zero)) { 
      case (map, (k, v)) => 
       map + (k -> (map(k) |+| v)) 
     } 
    } 
} 

[...] 

Ora, per la tua domanda attuale, non c'è bisogno di cambiare mergeKeys per gestire tipi divertenti di combinazioni; basta scrivere un Monoid per gestire qualsiasi tipo di combinazione che si desidera fare. Diciamo che si voleva fare le corde + Ints esempio:

implicit def monoidStringInt = new Monoid[(String, Int)] { 
    val zero = ("", 0) 
    def append(a: (String, Int), b: => (String, Int)) = (a, b) match { 
     case ((a1, a2), (b1, b2)) => (a1 + b1, a2 + b2) 
    } 
} 

println { 
    List(
     "a" -> ("Hello, ", 20), 
     "b" -> ("Goodbye, ", 30), 
     "a" -> ("World", 12) 
    ).mergeKeys 
} 

Map(a -> (Hello, World,32), b -> (Goodbye, ,30)) 
+0

Perfetto, grazie! – schmmd

Problemi correlati