Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 如何高效/优雅地提取连续整数的范围?_Scala_Range_Seq - Fatal编程技术网

Scala 如何高效/优雅地提取连续整数的范围?

Scala 如何高效/优雅地提取连续整数的范围?,scala,range,seq,Scala,Range,Seq,让我们从一个整数序列开始,如: val seq = List(1,2,3,4,5,6,9,10,11,14,15,16,18) 我想获得表示连续集合的成对序列,例如: val ranges = List(1,6,9,11,14,16,18,18) 替代格式Seq[(Int,Int)]也可以接受: val ranges = List((1,6),(9,11),(14,16),(18,18)) 说明: -范围1..6和11..16中的整数位于seq -整数18位于seq中,但没有后继者或前辈,

让我们从一个整数序列开始,如:

val seq = List(1,2,3,4,5,6,9,10,11,14,15,16,18)
我想获得表示连续集合的成对序列,例如:

val ranges = List(1,6,9,11,14,16,18,18)
替代格式
Seq[(Int,Int)]
也可以接受:

val ranges = List((1,6),(9,11),(14,16),(18,18))
说明: -范围
1..6
11..16
中的整数位于
seq
-整数
18
位于
seq
中,但没有后继者或前辈,因此它在
范围中显示为
18,18

请注意,单元素序列应始终成对报告,例如:

val seq = List(18, 19, 21)
其结果应为:

val ranges = List(18,19,21,21)
或者,如果您更喜欢Tuple2样式:

我想使用一个函数从
seq
导出
范围
;解决方案(由同事提供)是:

这确实很优雅,但我不确定效率,因为使用了
contains

在效率或优雅方面,谁能提供更好的解决方案

谢谢大家!

如果输入是有序的(或者您愿意先对其进行排序),您可以使用
foldLeft
在一次传递中非常简洁地完成这一操作(好的,两次传递的
相反的
,但这是处理列表的一个工件,如果您愿意放弃一些优雅,可以避免):

在这种情况下,我们得到以下信息:

res0: List[(Int, Int)] = List((1,6), (9,11), (14,16), (18,18))

对于每个元素,我们检查它是否是我们添加的最后一个范围的末尾的后续元素。如果是,我们更换该范围内的端部。如果不是这样,我们将开始一个新的系列。

类似于以下内容:

(None +: seq.toStream.map(x => Some(x)) :+ None).sliding(2).flatMap {
    case Seq(None, Some(b)) => List(b)
    case Seq(Some(a), None) => List(a)
    case Seq(Some(a), Some(b)) if b - a == 1 => Nil
    case Seq(Some(a), Some(b)) => List(a,b)
}.toList

请注意,为了效率起见,
a contains
可以在
toSet
版本的相同
Seq
a
上替换为
contains
;这可能会提高大型
Seq
s的性能,同时不会显著更改此代码。输入是否保证订购?@TravisBrown在我的情况下是的,但我们可以在搜索范围之前对Seq a应用排序方法。因此,我认为排序元素在任何情况下都是一个合理的开始条件。我已经澄清了预期的格式,并修复了结果中的错误(int范围
(9,11)
(14,16)
被破坏成……奇怪的东西:)@travisbrown来自@Dima和Travis Brown的两种解决方案对我来说都相当优雅:谢谢你们!我将对解决方案进行一些性能测试,以澄清在这方面哪一个更好,同时我可以想象Dima的解决方案可能有优势,因为它应该生成更少的中间对象,从而消除GC对非常大的整数列表的一些负担。请注意,此解决方案应适用于基于Spark的大型数据分析系统,其中数百万(或数十亿…)这样的列表(每个列表有十到几十万个点)必须同时进行分析,因此效率也很重要。Dima,代码中有一个小错误:如果您有
val seq=List(18,19,21)
,结果是
List(18,19,21)
,这并不好(应该是
List(18,19,21,21)
,因为元素计数应该始终是偶数)。我认为这是因为
flatMap
使用的列表在本例中是:
list(list(18),list(18,19),list(19,21))
,所以在复制中间列表(预平面映射)中的
list(19,21)
之后,就没有
21
(或
list(21)
)可供复制。你觉得怎么样?@logtwo是的,你是对的。它没有正确处理最后一个元素。现在修好了。在头部预先添加一个虚拟元素是不够的(最后需要相同的技巧),所以我将它映射到选项,这样我们就可以使用
None
作为哨兵。注意,将其转换为中间的流——避免了额外的遍历,从而可以一次完成所有的操作。
seq.foldLeft[List[(Int, Int)]](Nil) {
  case ((a, b) :: rest, i) if i == b + 1 => (a, i) :: rest
  case (acc, i) => (i, i) :: acc
}.reverse
res0: List[(Int, Int)] = List((1,6), (9,11), (14,16), (18,18))
(None +: seq.toStream.map(x => Some(x)) :+ None).sliding(2).flatMap {
    case Seq(None, Some(b)) => List(b)
    case Seq(Some(a), None) => List(a)
    case Seq(Some(a), Some(b)) if b - a == 1 => Nil
    case Seq(Some(a), Some(b)) => List(a,b)
}.toList