考虑以下列表(0,10,12,7,-10,7,2,3,-2,4)
我想有一个函数将上面的列表分组到函数(x:Int)=>x>0
为真的子列表中以获得所需的结果
((0,10,12,7),(7,2,3),(4))
我找到了pack函数,它将列表中的连续相同元素分组。
def pack[T](xs: List[T]): List[List[T]] = xs match {
case Nil => Nil
case x :: xs1 =>
val (first, rest) = xs span (y => x == y)
first :: pack(rest)
}
这几乎是我所需要的,但我无法将其扩展到我目前的问题。
答案 0 :(得分:8)
我想到的第一个天真的版本(肯定有改进的余地)
def pack(xs: List[Int]): List[List[Int]] = xs match {
case Nil => Nil
case _ =>
val (first, rest) = xs.span(_ >= 0)
first :: pack(rest.dropWhile(_ < 0))
}
示例:
scala> pack(List(0, 10, 12, 7, -10, 7, 2, 3, -2, 4))
res0: List[List[Int]] = List(List(0, 10, 12, 7), List(7, 2, 3), List(4))
答案 1 :(得分:7)
基于span的类似方法,在这种情况下,对于谓词后面的多个分区称为multiSpan;因此
val a = List(0,10,12,7,-10,7,2,3,-2,4)
否定谓词
a.multiSpan( _ < 0)
List(List(0, 10, 12, 7), List(-10, 7, 2, 3), List(-2, 4))
所以可以用
实现所需的输出a.multiSpan( _ < 0).map( _.dropWhile(_ < 0))
List(List(0, 10, 12, 7), List(7, 2, 3), List(4))
与陈述一起传达
def pack[T](xs: List[T]): List[List[T]] =
xs.multiSpan( _ < 0).map( _.dropWhile(_ < 0))
答案 2 :(得分:3)
您还可以将foldLeft与其他二进制变量一起使用,当下一个正元素应附加到最后一个列表时,该变量为true:
def pack(xs: List[Int], f: (Int => Boolean)): List[List[Int]] = {
xs.foldLeft((List[List[Int]](), false)) {
case ((acc, false), el) if f(el) => (acc :+ List(el), true)
case ((acc, true), el) if f(el) => (acc.init :+ (acc.last :+ el) , true)
case ((acc, _) , el) => (acc, false)
}._1
}
答案 3 :(得分:3)
如果每个人都在发布他们的版本,我也会添加自己的版本。
使用foldLeft
遍历列表一次,Vector
遍历列表以进行有效的功能追加。最终结果是List[List[A]]
,但是:
/**
* scala> val l = List(0, 10, 12, 7, -10, 7, 2, 3, -2, 4)
* scala> groupFilter(l)(_ >= 0)
* res0: List[List[Int]] = List(List(0, 10, 12, 7), List(7, 2, 3), List(4))
*/
def groupFilter[A](list: List[A])(predicate: A => Boolean): List[List[A]] = {
// The accumulator is a tuple containing the already grouped
// previous values and the the current group being built.
val seed = Vector.empty[List[A]] -> Vector.empty[A]
val (prevGroups, lastGroup) = list.foldLeft(seed) {
case (groups @ (prevGroups, lastGroup), a) =>
if (predicate(a))
prevGroups -> (lastGroup :+ a)
else if (lastGroup.nonEmpty)
(prevGroups :+ lastGroup.toList) -> Vector.empty
else
groups
}
(prevGroups :+ lastGroup.toList).toList
}
使用foldRight
和List
&#39; s ::
来降低时间复杂度的版本:
def groupFilter[A](list: List[A])(predicate: A => Boolean): List[List[A]] = {
val seed = List.empty[List[A]] -> List.empty[A]
val (prevGroups, lastGroup) = list.foldRight(seed) {
case (a, groups @ (prevGroups, lastGroup)) =>
if (predicate(a))
prevGroups -> (a :: lastGroup)
else if (lastGroup.nonEmpty)
(lastGroup :: prevGroups) -> List.empty
else
groups
}
lastGroup :: prevGroups
}
使用显式尾递归的最后一个版本:
def groupFilter[A](list: List[A])(predicate: A => Boolean): List[List[A]] = {
@annotation.tailrec
def loop(list: List[A], prevGroups: Vector[List[A]], lastGroup: Vector[A]): List[List[A]] = {
list match {
case Nil =>
(prevGroups :+ lastGroup.toList).toList
case head :: tail if predicate(head) =>
loop(tail, prevGroups, lastGroup :+ head)
case _ :: tail if lastGroup.nonEmpty =>
loop(tail, prevGroups :+ lastGroup.toList, Vector.empty)
case _ :: tail =>
loop(tail, prevGroups, lastGroup)
}
}
loop(list, Vector.empty, Vector.empty)
}