Scala将函数为true的列表中的连续元素组合在一起

时间:2015-02-02 20:24:19

标签: scala

考虑以下列表(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)
  }

这几乎是我所需要的,但我无法将其扩展到我目前的问题。

4 个答案:

答案 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
}

使用foldRightList&#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)
}