Scala:为什么mapValues产生一个视图,是否有任何稳定的select?

刚才我很惊讶地发现mapValues产生一个视图。 结果如下例所示:

 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 } 

这个想法是,我有一个分布,这是随着一些随机扰动,然后我重新规范化。 代码实际上失败了,因为mapValues产生一个view_ + 0.1 * rand.nextGaussian每当使用perturbed时总是重新评估_ + 0.1 * rand.nextGaussian

我现在正在做一些像distribution map { case (s, p) => (s, p + 0.1 * rand.nextGaussian) } ,但是这只是有点冗长。 所以这个问题的目的是:

  1. 提醒那些不知道这个事实的人。
  2. 找出他们为什么使mapValues输出view的原因。
  3. 是否有另一种生成混凝土Map方法。
  4. 有没有其他常用的收集方法有这个陷阱。

谢谢。

有一张关于这个的票, SI-4776 (由YT)。

介绍它的承诺有这样的说法:

在jrudolph的build议之后,使得filterKeysmapValues转换了抽象地图,并为不可变的地图提供了重复的function。 将transformfilterNot的移动到一般的地图。 由phaller审查。

我一直无法findjrudolph的原始build议,但我认为这样做是为了使mapValues更高效。 给出这个问题,这可能会是一个惊喜,但是如果你不可能多次迭代这个值,那么mapValues 更有效率。

作为解决方法,可以使用mapValues(...).view.force生成新的Map

Scala文档说:

将该映射的每个key映射到f(this(key))的映射视图。 结果地图包装原始地图而不复制任何元素。

所以这应该是可以预料的,但是这让我感到非常恐惧,明天我将不得不复习一堆代码。 我并不期待这样的行为:-(

只是另一个解决方法:

你可以调用toSeq来获得一个副本,如果你需要它返回映射到toMap ,但是这个不必要的创build对象,并且在使用map有一个性能含义

一个可以相对容易的写,一个mapValues不会创build视图,如果没有人在我面前,我会明天做,并在这里发布代码;)

编辑:

我发现了一个简单的方法来“强制”视图,在mapValues之后使用“.map(identity)”(所以不需要实现特定的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) 

这是一种耻辱,返回的types实际上不是一个视图! 另外一个人可以称之为“武力”。