2010-08-07 6 views
8

In Scala mi piacerebbe essere in grado di scrivereScala Immutabile MultiMap

val petMap = ImmutableMultiMap(Alice->Cat, Bob->Dog, Alice->Hamster) 

Il sottostante Map [proprietario, Set [Pet]] dovrebbe avere sia Mappa e Set immutabile. Ecco una prima bozza per ImmutibleMultiMap con oggetto companion:

import collection.{mutable,immutable} 

class ImmutableMultiMap[K,V] extends immutable.HashMap[K,immutable.Set[V]] 

object ImmutableMultiMap { 
    def apply[K,V](pairs: Tuple2[K,V]*): ImmutableMultiMap[K,V] = { 
    var m = new mutable.HashMap[K,mutable.Set[V]] with mutable.MultiMap[K,V] 
    for ((k,v) <- pairs) m.addBinding(k,v) 
    // How do I return the ImmutableMultiMap[K,V] corresponding to m here? 
    } 
} 

Riesci a risolvere la riga di commento con eleganza? Sia la mappa che i set dovrebbero diventare immutabili.

Grazie!

+1

Questo può essere utile come un esempio di come convertire un mutabile a una mappa immutabili: http://stackoverflow.com/questions/2817055/ converting-mutable-to-immutable-map –

risposta

3

Hai un problema più grande, perché non c'è alcun metodo in ImmutableMultiMap che restituirà un ImmutableMultiMap - quindi è impossibile aggiungere elementi ad esso, e il costruttore non fornisce il supporto per la creazione con elementi. Si prega di vedere le implementazioni esistenti e prestare attenzione all'oggetto builder dell'oggetto associato e ai metodi correlati.

+0

Grazie Daniel. Ho provato e decifrato le cose del costruttore, passando per il codice sorgente per l'oggetto companion su immutable.HashSet. Tuttavia, non riesco a dare un senso. Ti dispiacerebbe mostrarmi come risolvere il problema di costruire ImmutableMultiMap? – PerfectTiling

4

Ho riscritto questo stesso metodo due volte, in lavori successivi. :) Qualcuno davvero Oughta lo rende più generale. È comodo avere anche una versione completa.

/** 
    * Like {@link scala.collection.Traversable#groupBy} but lets you return both the key and the value for the resulting 
    * Map-of-Lists, rather than just the key. 
    * 
    * @param in the input list 
    * @param f the function that maps elements in the input list to a tuple for the output map. 
    * @tparam A the type of elements in the source list 
    * @tparam B the type of the first element of the tuple returned by the function; will be used as keys for the result 
    * @tparam C the type of the second element of the tuple returned by the function; will be used as values for the result 
    * @return a Map-of-Lists 
    */ 
    def groupBy2[A,B,C](in: List[A])(f: PartialFunction[A,(B,C)]): Map[B,List[C]] = { 

    def _groupBy2[A, B, C](in: List[A], 
          got: Map[B, List[C]], 
          f: PartialFunction[A, (B, C)]): Map[B, List[C]] = 
    in match { 
     case Nil => 
     got.map {case (k, vs) => (k, vs.reverse)} 

     case x :: xs if f.isDefinedAt(x) => 
     val (b, c) = f(x) 
     val appendTo = got.getOrElse(b, Nil) 
     _groupBy2(xs, got.updated(b, c :: appendTo), f) 

     case x :: xs => 
     _groupBy2(xs, got, f) 
    } 

    _groupBy2(in, Map.empty, f) 
    } 

E lo si utilizza in questo modo:

val xs = (1 to 10).toList 
groupBy2(xs) { 
    case i => (i%2 == 0, i.toDouble) 
} 

res3: Map[Boolean,List[Double]] = Map(false -> List(1.0, 3.0, 5.0, 7.0, 9.0),  
             true -> List(2.0, 4.0, 6.0, 8.0, 10.0)) 
+0

Ha usato questa risposta molte volte. Si noti che 'Seq' è più generale di un elenco e richiede semplicemente di cambiare le firme e convertire' c :: appendTo' in 'c +: seq'. Penso che la risposta sarebbe migliorata con l'aggiornamento a 'Seq'? – simbo1905