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) }
,但是这只是有点冗长。 所以这个问题的目的是:
- 提醒那些不知道这个事实的人。
- 找出他们为什么使
mapValues
输出view
的原因。 - 是否有另一种生成混凝土
Map
方法。 - 有没有其他常用的收集方法有这个陷阱。
谢谢。
有一张关于这个的票, SI-4776 (由YT)。
介绍它的承诺有这样的说法:
在jrudolph的build议之后,使得
filterKeys
和mapValues
转换了抽象地图,并为不可变的地图提供了重复的function。 将transform
和filterNot
的移动到一般的地图。 由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实际上不是一个视图! 另外一个人可以称之为“武力”。