问题
Suppose I am writing a function foo: Iterator[A] => Iterator[List[A]]
to transform a given iterator into an iterator of chunks of duplicates :
def foo[T](it: Iterator[A]): Iterator[List[A]] = ???
foo("abbbcbbe".iterator).toList.map(_.mkString) // List("a", "bbb", "c", "bb", "e")
In order to implement foo
I want to reuse function splitDupes: Iterator[A] => (List[A], Iterator[A])
that splits an iterator into a prefix with duplicates and the rest (thanks a lot to Kolmar who suggested it here)
def splitDupes[A](it: Iterator[A]): (List[A], Iterator[A]) = {
if (it.isEmpty) {
(Nil, Iterator.empty)
} else {
val head = it.next
val (dupes, rest) = it.span(_ == head)
(head +: dupes.toList, rest)
}
}
Now I am writing foo
using splitDupes
like that:
def foo[A](it: Iterator[A]): Iterator[List[A]] = {
if (it.isEmpty) {
Iterator.empty
} else {
val (xs, ys) = Iterator.iterate(splitDupes(it))(x => splitDupes(x._2)).span(_._2.nonEmpty)
(if (ys.hasNext) xs ++ Iterator(ys.next) else xs).map(_._1)
}
}
This implementation seems working but it's look complicated and clumsy.
How would you improve the foo
implementation above ?
回答1:
You can do it like this:
def foo[A](it: Iterator[A]): Iterator[List[A]] = {
Iterator.iterate(splitDupes(it))(x => splitDupes(x._2))
.map(_._1)
.takeWhile(_.nonEmpty)
}
The empty case is already handled in splitDupes
. You can safely keep calling splitDupes
until it hits this empty case (that is, starts returning Nil
in the first tuple element).
This works ok in all cases:
scala> foo("abbbcbbe".iterator).toList.map(_.mkString)
res1: List[String] = List(a, bbb, c, bb, e)
scala> foo("".iterator).toList.map(_.mkString)
res2: List[String] = List()
scala> foo("a".iterator).toList.map(_.mkString)
res3: List[String] = List(a)
scala> foo("aaa".iterator).toList.map(_.mkString)
res4: List[String] = List(aaa)
scala> foo("abc".iterator).toList.map(_.mkString)
res5: List[String] = List(a, b, c)
来源:https://stackoverflow.com/questions/60025718/transforming-an-iterator-into-an-iterator-of-chunks-of-duplicates