Scala: Why does mapValues โ€‹โ€‹create a view and are there any stable alternatives?

Now I am surprised to learn that mapValues creates a view. The corollary is shown in the following example:

 case class thing(id: Int) val rand = new java.util.Random val distribution = Map(thing(0) -> 0.5, thing(1) -> 0.5) val perturbed = distribution mapValues { _ + 0.1 * rand.nextGaussian } val sumProbs = perturbed.map{_._2}.sum val newDistribution = perturbed mapValues { _ / sumProbs } 

The idea is that I have a distribution that is outraged by some randomness, and then I renormalize it. The code doesnโ€™t actually work in its original intention: since mapValues creates a view , _ + 0.1 * rand.nextGaussian always reevaluated whenever perturbed used.

Now I am doing something like distribution map { case (s, p) => (s, p + 0.1 * rand.nextGaussian) } , but this is a bit verbose. Therefore, the purpose of this question is:

  • Remind people who do not know about this fact.
  • See why they infer mapValues view s.
  • Is there an alternative method that creates a specific Map .
  • Are there any other commonly used collection methods that have this trap.

Thank.

+44
scala map
Feb 14 '13 at 19:34
source share
2 answers

There's a ticket about it, SI-4776 (YT).

The end that introduces it has the following:

Following the suggestion of jrudolph, we made filterKeys and mapValues convert abstract maps and duplicate functionality for immutable maps. Moved transform and filterNot from immutable to community cards. Overview of phaller.

I could not find the original jrudolph suggestion, but I suppose it was done to make mapValues more efficient. Ask a question that may come as a surprise, but mapValues more effective if you are unlikely to mapValues over values โ€‹โ€‹more than once.

As a job, you can do mapValues(...).view.force to create a new Map .

+30
Feb 14 '13 at 20:10
source share

The scala doc document says:

a map map that maps each key this map to f(this(key)) . The resulting map wraps the original map without copying any elements.

So this was to be expected, but it really scares me, I will have to review a bunch of code tomorrow. I did not expect this behavior: - (

Just a workaround:

You can call toSeq to get a copy, and if you need to return it back to the toMap map, but these are unnecessary creation objects and have an idea about the effect on the use of map

It is relatively easy to write, a mapValues that does not create a view, I will do it tomorrow and post the code here if no one does it in front of me;)

EDIT:

I found an easy way to "force" the view, use ".map (identity)" after mapValues โ€‹โ€‹(so there is no need to implement a specific function):

 scala> val xs = Map("a" -> 1, "b" -> 2) xs: scala.collection.immutable.Map[java.lang.String,Int] = Map(a -> 1, b -> 2) scala> val ys = xs.mapValues(_ + Random.nextInt).map(identity) ys: scala.collection.immutable.Map[java.lang.String,Int] = Map(a -> 1315230132, b -> 1614948101) scala> ys res7: scala.collection.immutable.Map[java.lang.String,Int] = Map(a -> 1315230132, b -> 1614948101) 

It's a shame that the return type is not really a view! otherwise it could be called "power" ...

+10
Feb 14 '13 at 19:53
source share



All Articles