2010-12-08 40 views
17

Quelle est la manière idiomatique d'un getOrElseUpdate pour les instances immutables.Map ?. J'utilise l'extrait ci-dessous, mais il semble bavard et inefficaceidiomatique "get or else update" pour immutable.Map?

var map = Map[Key, Value]() 

def foo(key: Key) = { 
    val value = map.getOrElse(key, new Value) 
    map += key -> value 
    value 
} 
+2

'map + = clé -> valeur' -> Peut-être que vous voulez dire carte mutable.Map? –

+1

Je pense que vous voulez dire mutable.Map !? Vous utilisez + = dans votre code qui ne fonctionne pas pour immutable.Map. Pour mutable.Map, il y a getOrElseUpdate(). – michid

+3

@Vasil Remeniuk, @michid: Bien sûr, cela fonctionne. Lorsqu'il n'y a pas de méthode '+ =', le compilateur convertit l'expression en 'map = map + key -> value'. J'ai mis à jour la question pour clarifier la carte est un var – IttayD

Répondre

9

Permettez-moi de résumer votre problème:

  • Vous voulez appeler une méthode sur une structure de données immuables
  • Vous voulez revenir une valeur et réattribuer un var
  • Parce que la structure de données est immuable, vous devez
    • retourner une nouvelle structure de données immuable, ou
    • faire l'affectation dans la méthode, en utilisant une fermeture fournie

Alors, que ce soit votre signature doit ressembler à

def getOrElseUpdate(key: K): Tuple2[V, Map[K,V]] 
//... use it like 
val (v, m2) = getOrElseUpdate(k) 
map = m2 

ou

def getOrElseUpdate(key: K, setter: (Map[K,V]) => Unit): V 
//... use it like 
val v = getOrElseUpdate(k, map = _) 

Si vous pouvez vivre avec Une de ces solutions, vous pouvez ajouter votre propre version avec une conversion implicite, mais à en juger par les signatures seules, je ne pense pas que l'une de ces solutions soit dans la bibliothèque standard.

+0

Je suppose que de toutes les réponses cela signifie qu'il n'y a pas de façon idiomatique (autre que la suggestion de @ Daniel de travailler avec des objets immuables) – IttayD

+0

Le problème est que vous voulez réaffecter votre variable en faisant quelque chose d'autre. Il n'y a aucun moyen de faire cela à l'intérieur d'une autre méthode sans aide (par exemple, il devient moche). Vous auriez besoin d'un support approprié pour le passage par référence pour cela (ce qui n'est possible qu'avec certains hacks dans Scala). – Debilski

+1

Il est en fait assez facile d'obtenir une sémantique de référence par passage dans Scala: 'case class Var [T] (var réf: T) {def apply() = ref; def mise à jour (v: T) = ref = v} '. Idiomatique? N ° –

7

Il n'y a pas de telle sorte - mutation de la carte (mise à jour), lorsque vous obtenez une valeur de la carte, est un effet secondaire (qui contredit à immuabilité/fonctionnelle style de programmation).

Lorsque vous souhaitez effectuer une nouvelle carte immuable avec la valeur par défaut, si une autre valeur de la clé spécifiée n'existe pas, vous pouvez effectuer les opérations suivantes:

map + (key -> map.getOrElse(key, new Value)) 
+0

Votre code est lignes 1-2 dans mon 'foo', mais il retourne une carte, pas la valeur – IttayD

2

Pourquoi ne pas utiliser withDefault ou withDefaultValue si vous avez une carte immuable?

+1

avecDefault et withDefaultValue retournera une valeur par défaut, mais ne mettra pas à jour le carte. – IttayD

+0

@IttayD Et quelle différence cela fait-il? –

+1

@Daniel 'withDefault' et' withDefaultValue' ne donneront pas la même sémantique que le code de @ IttayD ci-dessus en ce qui concerne l'identité de l'objet.'withDefault' créerait de nouvelles valeurs potentiellement inégales sur plusieurs récupérations d'une clé donnée. 'withDefaultValue' renverrait une valeur particulière sur les récupérations de clés différentes. –

12

je serais probablement mettre en œuvre une méthode getOrElseUpdated comme ceci:

def getOrElseUpdated[K, V](m: Map[K, V], key: K, op: => V): (Map[K, V], V) = 
    m.get(key) match { 
    case Some(value) => (m, value) 
    case None => val newval = op; (m.updated(key, newval), newval) 
    } 

qui renvoie soit la carte d'origine si m a un mappage pour key ou d'une autre carte avec la cartographie key -> op ajouté. La définition de cette méthode est similaire à getOrElseUpdate de mutable.Map.

+0

vous êtes le seul à avoir réellement répondu à la question :-) – ib84