【问题标题】:Scala group consecutive elements in list where function is trueScala在函数为真的列表中对连续元素进行分组
【发布时间】:2015-02-03 05:30:38
【问题描述】:

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

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

【问题讨论】:

    标签: scala


    【解决方案1】:

    基于跨度的类似方法,在这种情况下称为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】:

      我想到的第一个幼稚版本(肯定有改进的余地)

      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))
      

      【讨论】:

        【解决方案3】:

        当下一个正元素应附加到最后一个列表时,您还可以将 foldLeft 与其他二进制变量一起使用:

        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
        }
        

        【讨论】:

          【解决方案4】:

          如果每个人都发布他们的版本,我也会添加我自己的。

          使用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:: 来保持较低的时间复杂度:

          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)
          }
          

          【讨论】:

            猜你喜欢
            • 2011-06-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-04-06
            • 2021-11-26
            • 2019-12-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多