scala (scanLeft) - 如何(功能上)获取具有累积值/频率的地图
scala (scanLeft) - how to (functionally) get a Map with cumulated values / frequency
我是 Scala 的新手,正在尝试从具有单个值的 Map 中获取具有累积频率作为值的 Map。所以(对于一个不可变的 Map 保持顺序,例如 ListMap)value(i) 是原始 Map 中直到并包括 i 的所有值的总和:
val map = ListMap(0.0 -> 0.1, 0.01 -> 0.2, 0.05 -> 0.3, 0.1 -> 0.4)
val resultMap = ListMap(0.0 -> 0.1, 0.01 -> 0.3, 0.05 -> 0.6, 0.1 -> 1.0)
因为需要访问以前的键值对,所以我想出了一个迭代实现,它使用 mutable.LinkedHashMap 和 运行 总数,但我发现那很乱而且 仍在寻找一种从不可变映射中获取可变映射的方法(如下面的第 3 行所示):
var cummulativeMap0 = mutable.LinkedHashMap.empty[Double, Double]
var total = 0.0
//line3:
val copyMap = mutable.LinkedHashMap(0.0->0.1, 0.01->0.2, 0.05->0.3, 0.1->0.4)
map.foreach {
(kv) => {
total = total + copyMap.remove(kv._1).get
cummulativeMap0.put(kv._1 , total)
}
}
cummulativeMap0
或者,我可以使用 scanLeft,但代价是将键和值拆分为 2 个列表,以便稍后压缩:
val cummValues = map.values.scanLeft(0.0){ (a, b)=>a+b }.tail
val cummmulativeMap2 = (map.keys zip cummValues).toMap
最idiomatic/functional实现此目的的方法是什么,直接在地图上使用 scanLeft 可行吗?请帮忙
如果你不想zip-unzip它,只需忽略每一步中的前一个键:
import scala.collection.immutable._
val map = ListMap(0.0 -> 0.1, 0.01 -> 0.2, 0.05 -> 0.3, 0.1 -> 0.4)
val res = map.scanLeft((0.0, 0.0)){
case ((_, acc), (x, y)) => (x, acc + y)
}
println(res)
给你:
ListMap(0.0 -> 0.1, 0.01 -> 0.3, 0.05 -> 0.6, 0.1 -> 1.0)
(达到机器精度,从输出中截断了一些零)
我相信您可以在地图的元素上向左扫描,在您的初始值中使用虚拟键:
val result = map.scanLeft((0.0, 0.0)){case ((_, av),(bk,bv)) => (bk, av + bv)}.tail
我想到了 zip+map,主要是因为我不知道 scanLeft。 :)
scala> val imap = List(0 -> 1, 1 -> 2, 5 -> 3, 1 -> 4)
map: List[(Int, Int)] = List((0,1), (1,2), (5,3), (1,4))
// add neutral element to start with for first element
scala> val m = imap.zip ((0,0) :: imap)
m: List[((Int, Int), (Int, Int))] = List(((0,1),(0,0)), ((1,2),(0,1)), ((5,3),(1,2)), ((1,4),(5,3)))
// same as in scanLeft:
scala> m.map {case ((a, b), (c, d)) => (a, b + d)}
res153: List[(Int, Int)] = List((0,1), (1,3), (5,5), (1,7))
(为简洁起见,使用整数)。
我是 Scala 的新手,正在尝试从具有单个值的 Map 中获取具有累积频率作为值的 Map。所以(对于一个不可变的 Map 保持顺序,例如 ListMap)value(i) 是原始 Map 中直到并包括 i 的所有值的总和:
val map = ListMap(0.0 -> 0.1, 0.01 -> 0.2, 0.05 -> 0.3, 0.1 -> 0.4)
val resultMap = ListMap(0.0 -> 0.1, 0.01 -> 0.3, 0.05 -> 0.6, 0.1 -> 1.0)
因为需要访问以前的键值对,所以我想出了一个迭代实现,它使用 mutable.LinkedHashMap 和 运行 总数,但我发现那很乱而且 仍在寻找一种从不可变映射中获取可变映射的方法(如下面的第 3 行所示):
var cummulativeMap0 = mutable.LinkedHashMap.empty[Double, Double]
var total = 0.0
//line3:
val copyMap = mutable.LinkedHashMap(0.0->0.1, 0.01->0.2, 0.05->0.3, 0.1->0.4)
map.foreach {
(kv) => {
total = total + copyMap.remove(kv._1).get
cummulativeMap0.put(kv._1 , total)
}
}
cummulativeMap0
或者,我可以使用 scanLeft,但代价是将键和值拆分为 2 个列表,以便稍后压缩:
val cummValues = map.values.scanLeft(0.0){ (a, b)=>a+b }.tail
val cummmulativeMap2 = (map.keys zip cummValues).toMap
最idiomatic/functional实现此目的的方法是什么,直接在地图上使用 scanLeft 可行吗?请帮忙
如果你不想zip-unzip它,只需忽略每一步中的前一个键:
import scala.collection.immutable._
val map = ListMap(0.0 -> 0.1, 0.01 -> 0.2, 0.05 -> 0.3, 0.1 -> 0.4)
val res = map.scanLeft((0.0, 0.0)){
case ((_, acc), (x, y)) => (x, acc + y)
}
println(res)
给你:
ListMap(0.0 -> 0.1, 0.01 -> 0.3, 0.05 -> 0.6, 0.1 -> 1.0)
(达到机器精度,从输出中截断了一些零)
我相信您可以在地图的元素上向左扫描,在您的初始值中使用虚拟键:
val result = map.scanLeft((0.0, 0.0)){case ((_, av),(bk,bv)) => (bk, av + bv)}.tail
我想到了 zip+map,主要是因为我不知道 scanLeft。 :)
scala> val imap = List(0 -> 1, 1 -> 2, 5 -> 3, 1 -> 4)
map: List[(Int, Int)] = List((0,1), (1,2), (5,3), (1,4))
// add neutral element to start with for first element
scala> val m = imap.zip ((0,0) :: imap)
m: List[((Int, Int), (Int, Int))] = List(((0,1),(0,0)), ((1,2),(0,1)), ((5,3),(1,2)), ((1,4),(5,3)))
// same as in scanLeft:
scala> m.map {case ((a, b), (c, d)) => (a, b + d)}
res153: List[(Int, Int)] = List((0,1), (1,3), (5,5), (1,7))
(为简洁起见,使用整数)。