使用Scala,如何获取包含另一个整数列表中子列表的开始和结束索引的元组列表?
考虑此列表:
val xs = List(3, 1, 1, 3, 5, 4, 4, 5)
有两个子列表,它们以相同的数字开头和结尾,并且在3, 1, 1, 3
和5, 4, 4, 5
之间具有较小的数字。
我想在元组列表中收集这些子列表的开始和结束索引。根据以上示例,该列表为:
List[(Int, Int)]((0, 3), (4, 7))
..因为第一个子集的起始索引为0并终止于3,第二个子集的索引为4和7。
执行此操作的优雅的Scala函数是什么?
编辑:我知道使用var
和for循环执行此命令的必要方法。我有兴趣为该问题找到一种优雅的功能解决方案。
答案 0 :(得分:2)
这是“单线”解决方案:
xs.zipWithIndex
.groupBy(_._1)
.mapValues(_.map(_._2).combinations(2).map(c => (c(0), c(1))).toList)
.toList
.flatMap { case (x, intervals) =>
intervals.filter{ case (a, b) => xs.slice(a, b).exists(_ < x)}
}
产生:
List((4,7), (0,3))
希望是可读性更高的版本,具有有意义的中间结果:
val numbersToIndices = xs.zipWithIndex.groupBy(_._1).mapValues(_.map(_._2))
val groupedIntervalsAsLists = numbersToIndices.mapValues(_.combinations(2).toList)
val groupedIntervals = intervalsAsLists.mapValues(_.map(v => (v(0), v(1)))).toList
val valleys = for {
(x, intervals) <- groupedIntervals
(a, b) <- intervals
if xs.slice(a, b).exists(_ < x)
} yield (a, b)
println(valleys)
再次发现
List((4,7), (0,3))
答案 1 :(得分:0)
该实现如何?
xs.foldLeft[((Int, Int), Option[Int], List[(Int, Int)])]((0, 0), None, Nil) {
case ((window, boundOption, result), value) =>
boundOption match {
case Some(bound) =>
if (bound != value)
((window._1, window._2 + 1), Some(bound), result)
else
((window._2 + 1, window._2 + 1), None, window :: result)
case None => ((window._1, window._2 + 1), Some(value), result)
}
}._3.reverse