如何在Scala中使用带有Iterator的takeWhile

ton*_*ian 9 iteration iterator scala

我有一个元素的迭代器,我想消耗它们,直到下一个元素满足条件,如:

val it = List(1,1,1,1,2,2,2).iterator
val res1 = it.takeWhile( _ == 1).toList
val res2 = it.takeWhile(_ == 2).toList
Run Code Online (Sandbox Code Playgroud)

res1给出一个预期List(1,1,1,1)res2返回,List(2,2)因为迭代器必须检查位置4中的元素.

我知道列表将被订购,所以没有必要像遍历那样遍历整个列表partition.我希望在条件不满足时立即完成.是否有任何聪明的方法与迭代器一起做这个?我不能toList对迭代器做一个因为它来自一个非常大的文件.

Wal*_*ski 6

我发现的最简单的解决方案:

val it = List(1,1,1,1,2,2,2).iterator
val (r1, it2) = it.span( _ == 1)

println(s"group taken is: ${r1.toList}\n rest is: ${it2.toList}")
Run Code Online (Sandbox Code Playgroud)

输出:

group taken is: List(1, 1, 1, 1)
rest is: List(2, 2, 2)
Run Code Online (Sandbox Code Playgroud)

很短,但进一步你必须使用新的迭代器。

对于任何不可变集合,它都是类似的:

  • 当您只需要集合的某些前缀时,请使用 takeWhile ,
  • 当您需要休息时也可以使用span。


oxb*_*kes 3

根据我的其他答案(我将其分开,因为它们基本上不相关),我认为您可以groupWhenIterator如下方式实施:

def groupWhen[A](itr: Iterator[A])(p: (A, A) => Boolean): Iterator[List[A]] = {
  @annotation.tailrec 
  def groupWhen0(acc: Iterator[List[A]], itr: Iterator[A])(p: (A, A) => Boolean): Iterator[List[A]] = {
    val (dup1, dup2) = itr.duplicate
    val pref = ((dup1.sliding(2) takeWhile { case Seq(a1, a2) => p(a1, a2) }).zipWithIndex collect {
      case (seq, 0)       => seq
      case (Seq(_, a), _) => Seq(a)
    }).flatten.toList
    val newAcc = if (pref.isEmpty) acc else acc ++ Iterator(pref)
    if (dup2.nonEmpty)
      groupWhen0(newAcc, dup2 drop (pref.length max 1))(p)
    else newAcc
  }
  groupWhen0(Iterator.empty, itr)(p)
}
Run Code Online (Sandbox Code Playgroud)

当我在示例上运行它时:

println( groupWhen(List(1,1,1,1,3,4,3,2,2,2).iterator)(_ == _).toList )
Run Code Online (Sandbox Code Playgroud)

我明白了List(List(1, 1, 1, 1), List(2, 2, 2))