【问题标题】:Reduce sequence by parts按部分减少序列
【发布时间】:2019-06-22 08:40:41
【问题描述】:

我有一个序列Seq[T],我想做部分归约。例如,对于Seq[Int],我想得到由单调区域的最长部分和组成的Seq[Int]。例如:

val s = Seq(1, 2, 4, 3, 2, -1, 0, 6, 8)
groupMonotionic(s) = Seq(1 + 2 + 4, 3 + 2 + (-1), 0 + 6 + 8)

我正在寻找一些方法,例如带有签名 fold(z: B)((B, T) => B, (T, T) => Boolean) 的条件折叠,其中谓词说明了在何处终止当前总和聚合,但似乎在 Seq 的子特征层次结构中没有类似的东西。

使用 Scala Collection API 而不使用可变变量的解决方案是什么?

【问题讨论】:

    标签: scala collections


    【解决方案1】:

    这是众多方法中的一种(使用Scala 2.13's List#unfold):

    // val items = Seq(1, 2, 4, 3, 2, -1, 0, 6, 8)
    items match {
      case first :: _ :: _ =>                // If there are more than 2 items
        List
          .unfold(items.sliding(2).toList) { // We slid items to work on pairs of consecutive items
            case Nil =>                      // No more items to unfold
              None                           // None signifies the end of the unfold
            case rest @ Seq(a, b) :: _ =>    // We span based on the sign of a-b
              Some(rest.span(x => (x.head - x.last).signum == (a-b).signum))
          }
          .map(_.map(_.last))                // back from slided pairs
          match { case head :: rest => (first :: head) :: rest }
      case _ =>                              // If there is 0 or 1 item
        items.map(List(_))
    }
    // List(List(1, 2, 4), List(3, 2, -1), List(0, 6, 8))
    

    只要展开函数提供SomeList.unfold 就会迭代。它以初始状态开始,该状态是要展开的项目列表。在每次迭代中,我们span 状态(要展开的剩余元素)基于标题两个元素差异的符号。展开的元素是具有相同单调性的标题项,展开状态成为其他剩余元素。

    List#span 将列表拆分为一个元组,其第一部分包含与应用的谓词匹配的元素,直到谓词不再有效。元组的第二部分包含其余元素。这完全符合List.unfold 的展开函数的预期返回类型,即Option[(A, S)](在本例中为Option[(List[Int], List[Int])])。

    Int.signum 返回 -101,具体取决于所应用的整数的符号。

    请注意,第一项必须放回结果中,因为它没有祖先确定其符号 (match { case head :: rest => (first :: head) :: rest })。

    要应用归约函数(在本例中为求和),我们可以映射最终结果:.map(_.sum)

    【讨论】:

      【解决方案2】:

      在 Scala 2.13+ 中使用 cats

      import scala.util.chaining._
      import cats.data._
      import cats.implicits._
      
      val s = List(1, 2, 4, 3, 2, -1, 0, 6, 8)
      
      def isLocalExtrema(a: List[Int]) =
          a.max == a(1) || a.min == a(1)
      
      implicit class ListOps[T](ls: List[T]) {
        def multiSpanUntil(f: T => Boolean): List[List[T]] = ls.span(f) match {
          case (h, Nil) => List(h)
          case (h, t) => (h ::: t.take(1)) :: t.tail.multiSpanUntil(f)
        }
      }
      
      def groupMonotionic(groups: List[Int]) = groups match {
        case Nil => Nil
        case x  if x.length < 3 => List(groups.sum)      
        case _ =>
      
          groups
            .sliding(3).toList
            .map(isLocalExtrema)
            .pipe(false :: _ ::: List(false))
            .zip(groups)
            .multiSpanUntil(!_._1)
            .pipe(Nested.apply)
            .map(_._2)
            .value
            .map(_.sum)
      
      }
      
      println(groupMonotionic(s))
      //List(7, 4, 14)
      

      【讨论】:

        【解决方案3】:

        这是一种使用foldLeft 遍历数字列表的方法,其中包含存储previous elementprevious trend 的Tuple3 累加器(listOfLists, prevElem, prevTrend) 以在当前迭代中有条件地组装list of lists

        val list = List(1, 2, 4, 3, 2, -1, 0, 6, 8)
        
        val isUpward = (a: Int, b: Int) => a < b
        
        val initTrend = isUpward(list.head, list.tail.head)
        
        val monotonicLists = list.foldLeft( (List[List[Int]](), list.head, initTrend) ){
          case ((lol, prev, prevTrend), curr) =>
            val currTrend = isUpward(curr, prev)
            if (currTrend == prevTrend)
              ((curr :: lol.head) :: lol.tail , curr, currTrend)
            else
              (List(curr) :: lol , curr, currTrend)
        }._1.reverse.map(_.reverse)
        // monotonicLists: List[List[Int]] = List(List(1, 2, 4), List(3, 2, -1), List(0, 6, 8))
        

        总结各个嵌套列表:

        monotonicLists.map(_.sum)
        // res1: List[Int] = List(7, 4, 14)
        

        【讨论】:

          猜你喜欢
          • 2019-06-15
          • 2020-11-14
          • 2018-09-06
          • 2013-11-12
          • 2020-04-12
          • 2014-10-27
          • 2020-05-11
          • 2018-04-24
          • 1970-01-01
          相关资源
          最近更新 更多