Scala:为什么mapValues会生成一个视图,是否有任何稳定的替代方案?

Kan*_*ane 59 scala map

刚才我很惊讶地发现了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 }
Run Code Online (Sandbox Code Playgroud)

我的想法是,我有一个分布,它有一些随机性,然后我重新规范它.代码实际上失败了它的初衷:因为mapValues生成a view,_ + 0.1 * rand.nextGaussian总是在perturbed使用时重新评估.

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

  1. 提醒那些不了解这一事实的人.
  2. 寻找他们mapValues输出view的原因.
  3. 是否有另一种生产混凝土的方法Map.
  4. 是否有任何其他常用的收集方法有这个陷阱.

谢谢.

Dan*_*ral 37

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

引入它的提交有这样的说法:

遵循jrudolph的建议,制作filterKeysmapValues 转换抽象地图,以及不可变地图的重复功能.移动transformfilterNot从不可变到一般地图.由phaller审查.

我无法找到jrudolph的原始建议,但我认为这样做是为了mapValues提高效率.提出问题,这可能会让人感到意外,但如果您不太可能多次迭代这些值,mapValues 则会更有效率.

作为一种解决方法,人们可以做mapValues(...).view.force一个新的Map.

  • 很好,但我仍然想知道为什么mapValues不会直接返回视图以使其更明确? (5认同)
  • @AloisCochard正如你在票中看到的那样,这正是我所要求的.它将具有直接提供`force`方法的进一步好处. (3认同)

Alo*_*ard 12

斯卡拉文件说:

地图视图,将每个key地图映射到f(this(key)).生成的地图包装原始地图而不复制任何元素.

所以这应该是预期的,但这让我很害怕,明天我将不得不回顾一堆代码.我没想到会有这样的行为:-(

只是另一种解决方法:

您可以调用toSeq以获取副本,如果需要它可以返回到映射toMap,但是这个不必要的创建对象,并且具有使用性能的影响map

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

编辑:

我发现了一种"强制"视图的简单方法,在mapValues之后使用'.map(identity)'(因此不需要实现特定的功能):

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)
Run Code Online (Sandbox Code Playgroud)

遗憾的是返回的类型实际上并不是一个视图!其他人本来可以称之为"武力"......

  • 这令人惊讶.视图的地图如何返回具体的地图而不是视图?这是不一致的.这完全没有考虑.从源代码确认这个东西在2.12中仍然存在. (2认同)