Tout à l’heure, je suis surpris d’apprendre que mapValues
produit une vue. La conséquence est illustrée dans l’exemple suivant:
case class thing(id: Int) val rand = new java.util.Random val dissortingbution = Map(thing(0) -> 0.5, thing(1) -> 0.5) val perturbed = dissortingbution mapValues { _ + 0.1 * rand.nextGaussian } val sumProbs = perturbed.map{_._2}.sum val newDissortingbution = perturbed mapValues { _ / sumProbs }
L’idée est que j’ai une dissortingbution qui est perturbée avec un certain hasard alors je la renormalise. Le code échoue en réalité dans son intention initiale: puisque mapValues
produit une view
, _ + 0.1 * rand.nextGaussian
est toujours réévalué chaque fois que des perturbed
sont utilisées.
Je suis en train de faire quelque chose comme la dissortingbution map { case (s, p) => (s, p + 0.1 * rand.nextGaussian) }
, mais c’est juste un peu verbeux. Donc, le but de cette question est:
mapValues
sortie de mapValues
s. Map
concrète. Merci.
Il y a un billet à ce sujet, SI-4776 (par YT).
Le commit qui l’introduit a ceci à dire:
Suite à une suggestion de jrudolph,
filterKeys
etmapValues
transformé des cartes abstraites et des fonctionnalités dupliquées pour des cartes immuables.transform
etfilterNot
immuable aux cartes générales. Révision par phaller.
Je n’ai pas pu trouver la suggestion originale de jrudolph, mais je suppose que cela a été fait pour rendre mapValues
plus efficace. Posez la question, cela peut être une surprise, mais mapValues
est plus efficace si vous n’êtes pas susceptible de répéter plusieurs fois les valeurs.
En guise de mapValues(...).view.force
, on peut faire mapValues(...).view.force
pour produire une nouvelle Map
.
Le scala doc dit:
une vue cartographique qui mappe chaque
key
de cette carte surf(this(key))
. La carte résultante enveloppe la carte d’origine sans copier d’éléments.
Donc, cela devrait être prévu, mais cela me fait beaucoup peur, je vais devoir revoir des tas de codes demain. Je ne m’attendais pas à un tel comportement 🙁
Juste une autre solution de contournement:
Vous pouvez appeler toSeq
pour obtenir une copie, et si vous en avez besoin de nouveau pour mapper sur toMap
, mais cela inutile de créer des objects et d’avoir une implication de performance sur l’utilisation de map
On peut relativement facilement écrire, une mapValues
qui ne crée pas de vue, je le ferai demain et posterai le code ici si personne ne le fait avant moi;)
MODIFIER:
J’ai trouvé un moyen simple de “forcer” la vue, utiliser “.map (identity)” après mapValues (donc pas besoin d’implémenter une fonction spécifique):
scala> val xs = Map("a" -> 1, "b" -> 2) xs: scala.collection.immutable.Map[java.lang.Ssortingng,Int] = Map(a -> 1, b -> 2) scala> val ys = xs.mapValues(_ + Random.nextInt).map(identity) ys: scala.collection.immutable.Map[java.lang.Ssortingng,Int] = Map(a -> 1315230132, b -> 1614948101) scala> ys res7: scala.collection.immutable.Map[java.lang.Ssortingng,Int] = Map(a -> 1315230132, b -> 1614948101)
C’est dommage que le type retourné ne soit pas une vue! Sinon, on aurait pu appeler «force» …