考虑Scala中的以下列表:
List(4, 5, 6, 7, 8, 12, 13, 14, 17, 23, 24, 25)
我想获得输出
List(List(4,8), List(12,14), List(17), List(23,25))
我有这个答案Scala List function for grouping consecutive identical elements
但它正在将相同的元素分组到同一个List中。
如何扩展此解决方案以解决我当前的问题?
我试过这段代码
def sliceByRange[A <% Int](s: List[A]): List[List[A]] = s match {
case Nil => Nil
case x :: xs1 =
val (first, rest) = s.span(y => y - x == 1)
first :: sliceByRange(rest)
}
但它没有用。
答案 0 :(得分:1)
请注意,您也可以使用List[(Int,Int)]
作为结果类型而不是List[List[Int]]
。这将反映出结果是List
范围更合适的事实。当然,对于单身范围,您无法将List(x,x)
转换为List(x)
。但是我希望无论如何这会让你再次咬你。
import scala.annotation.tailrec
@tailrec
def split(in: List[Int], acc: List[List[Int]] = Nil): List[List[Int]] = (in,acc) match {
case (Nil,a) => a.map(_.reverse).reverse
case (n :: tail, (last :: t) :: tt) if n == last + 1 => split(tail, (n :: t) :: tt)
case (n :: tail, a ) => split(tail, (n :: n :: Nil) :: a)
}
val result = split(List(4, 5, 6, 7, 8, 12, 13, 14, 17, 23, 24, 25))
println(result)
println("removing duplicates:")
println(result.map{
case List(x,y) if x == y => List(x)
case l => l
})
List(List(4, 8), List(12, 14), List(17, 17), List(23, 25))
removing duplicates:
List(List(4, 8), List(12, 14), List(17), List(23, 25))
答案 1 :(得分:1)
这是另一个例子:
val myList = List(4, 5, 7, 8, 12, 13, 14, 17, 23, 24, 25)
def partition(list: List[Int]): (List[Int], List[Int]) = {
val listPlusOne = (list.head - 1 :: list) // List(1,2,5) => List(0, 1, 2, 5)
val zipped = list zip listPlusOne // zip List(1,2,5) with List(0, 1, 2, 5) => List((1,0), (2,1), (5,2))
val (a, b) = zipped span { case (a, b) => b + 1 == a } // (List((1,0), (2,1)), List((5,2)))
(a.map(_._1), b.map(_._1)) // (List(1, 2),List(5))
}
def group(list: List[Int]): List[List[Int]] = list match {
case Nil => Nil
case _ =>
val (a, b) = partition(list)
val listA = List(List(a.head, a.last).distinct) // remove middle numbers..
val listB = if (b.isEmpty) Nil else group(b)
listA ++ listB
}
println(group(myList))
有点复杂,但有效......
答案 2 :(得分:0)
解释你引用的问题的答案:
def split(list: List[Int]) : List[List[Int]] = list match {
case Nil => Nil
case h::t =>
val segment = list.zipWithIndex.takeWhile { case (v, i) => v == h+i }.map(_._1)
List(h, segment.last).distinct :: split(list drop segment.length)
}
使用zipWithIndex
检查每个元素是否正好是“下一个”整数(数字应该与索引一起“增加”)。然后 - 仅获取段的“边界”并递归地移动到列表的其余部分。
答案 3 :(得分:0)
我的解决方案:
int
用法:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<defaultDocument>
<files>
<clear />
<add value="index.php" />
</files>
</defaultDocument>
<rewrite>
<rules>
<clear />
<rule name="WordPress Rule" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="index.php" />
</rule>
</rules>
<outboundRules>
<clear />
</outboundRules>
</rewrite>
</system.webServer>
</configuration>
如果希望保留中间值,可以使用以下示例:
def sliceByRange(items: List[Int]) =
items.sorted.foldLeft(Nil: List[List[Int]]) {
case (initRanges :+ (head :: Nil), next) if head == next - 1 =>
initRanges :+ (head :: next :: Nil) // append element to the last range
case (initRanges :+ (head :: last :: Nil), next) if last == next - 1 =>
initRanges :+ (head :: next :: Nil) // replace last range
case (ranges, next) =>
ranges :+ (next :: Nil) // add new range
}
用法:
sliceByRange(List(1, 2, 3, 5, 8, 9, 12, 13, 14, 19))
// List(List(1, 3), List(5), List(8, 9), List(12, 14), List(19))
当范围大小至少为3个元素时:
def makeSegments(items: List[Int]) =
items.sorted.foldLeft(Nil: List[List[Int]]) {
case (initSegments :+ lastSegment, next) if lastSegment.last == next - 1 =>
initSegments :+ (lastSegment :+ next)
case (segments, next) =>
segments :+ (next :: Nil)
}
用法(请注意(8,9)现在不在范围内):
makeSegments(List(1, 2, 3, 5, 8, 9, 12, 13, 14, 19))
// List(List(1, 2, 3), List(5), List(8, 9), List(12, 13, 14), List(19))
您可以定义def sliceByRange3elements(items: List[Int]) =
items.sorted.foldLeft(Nil: List[List[Int]]) {
case (initRanges :+ (head :: last :: Nil), next) if last == next - 1 =>
initRanges :+ (head :: next :: Nil) // replace last range
case (initRanges :+ (ll :: Nil) :+ (l :: Nil), next) if ll == next - 2 && l == next - 1 =>
initRanges :+ (ll :: next :: Nil) // make new range
case (ranges, next) =>
ranges :+ (next :: Nil)
}
方法以获得更多视觉输出:
sliceByRange3elements(List(1, 2, 3, 5, 8, 9, 12, 13, 14, 19))
// List(List(1, 3), List(5), List(8), List(9), List(12, 14), List(19))