2010-08-07 52 views
8

Scala Je voudrais pouvoir écrireScala Immuable MultiMap

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

La carte sous-jacente [propriétaire, Set [Pet]] doit avoir à la fois Carte et Set immuable. Voici un premier projet de ImmutibleMultiMap avec un objet compagnon:

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

Pouvez-vous résoudre la ligne de commentaire avec élégance? La carte et les ensembles devraient devenir immuables.

Merci!

+1

Cela peut être utile comme un exemple de la façon de convertir un mutable à une carte immuable: http://stackoverflow.com/questions/2817055/ conversion-mutable-à-immuable-carte –

Répondre

3

Vous avez un plus gros problème car il n'y a aucune méthode dans ImmutableMultiMap qui renverra un ImmutableMultiMap - il est donc impossible d'y ajouter des éléments, et le constructeur ne fournit pas de support pour la création d'éléments. Veuillez consulter les implémentations existantes et faites attention aux méthodes associées à l'objet compagnon builder.

+0

Merci Daniel. J'ai essayé de déchiffrer les choses du constructeur, en passant par le code source de l'objet compagnon pour immutable.HashSet. Cependant, je ne peux pas comprendre cela. Pourriez-vous me montrer comment vous résoudriez le problème de la construction du ImmutableMultiMap souhaité? – PerfectTiling

4

J'ai réécrit cette même méthode deux fois maintenant, lors de travaux successifs. :) Quelqu'un vraiment Oughta le rendre plus général. C'est pratique d'avoir une version complète aussi.

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

Et vous l'utilisez comme ceci:

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

Utilisé cette réponse plusieurs fois. Notez que 'Seq' est plus général que list et nécessite juste de changer les signatures et de convertir' c :: appendTo' en 'c +: seq'. Je pense que la réponse serait améliorée en passant à Seq? – simbo1905