计算列表中的数字在 Scala 的元组间隔列表中出现的次数

Count how many times numbers from a list occur in a list of tupled intervals in Scala

假设我有一个元组列表:

val ranges= List((1,4), (5,8), (9,10))

和一个数字列表

val nums = List(2,2,3,7,8,9)

我想制作一个从范围内的元组到 nums 中的给定数字落入该元组区间的次数的映射。

输出:

Map ((1,4) -> 3, (5,8) -> 2, (9,10) -> 1)

在 Scala 中实现它的最佳方法是什么

我一直在尝试使用 for 循环并保持计数器,但我做不到。

像这样:

val ranges = List((1, 4), (5, 8), (9, 10))
val nums = List(2, 2, 3, 7, 8, 9)

val occurences = ranges.map { case (l, r) => nums.count((l to r) contains _) }
val map = (ranges zip occurences).toMap

println(map) // Map((1,4) -> 3, (5,8) -> 2, (9,10) -> 1)

基本上它首先计算出现的次数,[3, 2, 1]。从那里很容易构建地图。它计算出现次数的方式是:

  • 浏览范围列表
  • 将每个范围转换为该范围的出现次数,这样做是这样的:
    • 该范围内包含列表 nums 中的多少个数字?

这是一个有效的单遍解决方案:

ranges
  .map(r => r -> nums.count(n => n >= r._1 && n <= r._2))
  .toMap

这避免了创建数字列表然后在单独的步骤中将它们与范围压缩的开销。

这是一个使用了更多 Scala 特性但有点太花哨的版本:

(for {
  r <- ranges
  range = r._1 to r._2
} yield r -> nums.count(range.contains)
).toMap

这也不太有效,因为 contains 必须允许具有步长值的范围,因此更加复杂。


这是一个更高效的版本,它避免了任何临时数据结构:

val result: Map[(Int, Int), Int] =
  ranges.map(r => r -> nums.count(n => n >= r._1 && n <= r._2))(collection.breakOut)

如果您不熟悉,请参阅此 explanation of breakOut。使用 breakOut 意味着 map 调用将直接构建 Map 而不是创建必须使用 [=20= 转换为 MapList ].