【问题标题】:How to group a variable-length, repeating sequence in Scala如何在Scala中对可变长度的重复序列进行分组
【发布时间】:2011-07-23 13:40:05
【问题描述】:

我有一组以某种模式重复的整数:

val repeatingSequence = List(1,2,3,1,2,3,4,1,2,1,2,3,4,5)

当模式重复时,我想将该列表分段;在这种情况下,当序列回到 1 时:

val groupedBySequence = List(List(1,2,3), List(1,2,3,4), List(1,2), List(1,2,3,4,5))

请注意,当序列跳回 1 时,我正在分组,但序列可以是任意长度。我和我的同事通过添加一个名为“groupWhen”的附加方法解决了这个问题

class IteratorW[A](itr: Iterator[A]) {
  def groupWhen(fn: A => Boolean): Iterator[Seq[A]] = {
    val bitr = itr.buffered
    new Iterator[Seq[A]] {
      override def hasNext = bitr.hasNext
      override def next = {
        val xs = collection.mutable.ListBuffer(bitr.next)
        while (bitr.hasNext && !fn(bitr.head)) xs += bitr.next
        xs.toSeq
      }
    }
  }
}
implicit def ToIteratorW[A](itr: Iterator[A]): IteratorW[A] = new IteratorW(itr)

> repeatingSequence.iterator.groupWhen(_ == 1).toSeq
List(List(1,2,3), List(1,2,3,4), List(1,2), List(1,2,3,4,5))

但是,我们都觉得收藏库中隐藏着一个更优雅的解决方案。

【问题讨论】:

  • 您使用的是哪个版本的 Scala?
  • List(2).iterator.groupWhen(_ == 1) 的结果应该是什么?
  • Thomas Jung - 我认为应该返回一个空列表。该示例中没有以 1 开头的组,因此应该将其丢弃。

标签: scala scala-collections


【解决方案1】:

给定一个迭代器itr,这样就可以解决问题:

val head = iter.next()
val out = (
  Iterator continually {iter takeWhile (_ != head)}
  takeWhile {!_.isEmpty}
  map {head :: _.toList}
).toList

【讨论】:

  • 处理 takeWhile 损失头部值的好方法 (issues.scala-lang.org/browse/SI-3581)。如果你放弃最终的 toList,它会在无限迭代器上工作。
  • 如果iter.hasNext == false怎么办?
  • huynhjl - 实际上,您需要对此进行明确检查,不过我不想分散核心逻辑的注意力 :)
【解决方案2】:

众所周知,折叠可以做任何事情... ;)

  val rs = List(1,2,3,1,2,3,4,1,2,1,2,3,4,5)
  val res = (rs++List(1)).foldLeft((List[List[Int]](),List[Int]()))((acc,e) => acc match {
    case (res,subl) => {
      if (e == 1) ((subl.reverse)::res,1::Nil) else (res, e::subl)
    }
  })
  println(res._1.reverse.tail)

请将此视为混淆 Scala 竞赛的参赛作品,而不是真正的答案。

【讨论】:

    【解决方案3】:

    这是我使用span 抨击的一个不完全优雅的解决方案:

    def groupWhen[A](fn: A => Boolean)(xs: List[A]): List[List[A]] = {
      xs.span(!fn(_)) match {
        case (Nil, Nil) => Nil
        case (Nil, z::zs) => groupWhen(fn)(zs) match {
          case ys::yss => (z::ys) :: yss
          case Nil => List(List(z))
        }
        case (ys, zs) => ys :: groupWhen(fn)(zs)
      }
    }
    
    scala> groupWhen[Int](_==1)(List(1,2,3,1,2,3,4,1,2,3,4,5))
    res39: List[List[Int]] = List(List(1, 2, 3), List(1, 2, 3, 4), List(1, 2, 3, 4, 5))
    
    scala> groupWhen[Int](_==1)(List(5,4,3,2,1,2,3,1,2,3,4,1,2,3,4,5))
    res40: List[List[Int]] = List(List(5, 4, 3, 2), List(1, 2, 3), List(1, 2, 3, 4), List(1, 2, 3, 4, 5))
    

    【讨论】:

      【解决方案4】:
      import scala.collection.mutable.ListBuffer
      import scala.collection.breakOut
      
      val repeatingSequence = List(1,2,3,1,2,3,4,1,2,1,2,3,4,5)
      val groupedBySequence: List[List[Int]] = repeatingSequence.foldLeft(ListBuffer[ListBuffer[Int]]()) {
        case (acc, 1) => acc += ListBuffer(1)
        case (acc, n) => acc.last += n; acc
      }.map(_.toList)(breakOut)
      

      【讨论】:

        猜你喜欢
        • 2017-10-29
        • 2017-10-19
        • 1970-01-01
        • 1970-01-01
        • 2021-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-02
        相关资源
        最近更新 更多