我也有类似的需求,但是@oxbow_lakes 的解决方案没有考虑到列表只有一个元素的情况,或者即使列表包含不重复的元素。此外,该解决方案不适合无限迭代器(它希望在给出结果之前“查看”所有元素)。
我需要的是对匹配谓词的顺序元素进行分组的能力,但也包括单个元素(如果我不需要它们,我总是可以将它们过滤掉)。我需要不断地交付这些组,而不必等待原始迭代器在它们被生产出来之前被完全消耗掉。
我想出了以下适合我需要的方法,并认为我应该分享:
implicit class IteratorEx[+A](itr: Iterator[A]) {
def groupWhen(p: (A, A) => Boolean): Iterator[List[A]] = new AbstractIterator[List[A]] {
val (it1, it2) = itr.duplicate
val ritr = new RewindableIterator(it1, 1)
override def hasNext = it2.hasNext
override def next() = {
val count = (ritr.rewind().sliding(2) takeWhile {
case Seq(a1, a2) => p(a1, a2)
case _ => false
}).length
(it2 take (count + 1)).toList
}
}
}
以上使用了一些辅助类:
abstract class AbstractIterator[A] extends Iterator[A]
/**
* Wraps a given iterator to add the ability to remember the last 'remember' values
* From any position the iterator can be rewound (can go back) at most 'remember' values,
* such that when calling 'next()' the memoized values will be provided as if they have not
* been iterated over before.
*/
class RewindableIterator[A](it: Iterator[A], remember: Int) extends Iterator[A] {
private var memory = List.empty[A]
private var memoryIndex = 0
override def next() = {
if (memoryIndex < memory.length) {
val next = memory(memoryIndex)
memoryIndex += 1
next
} else {
val next = it.next()
memory = memory :+ next
if (memory.length > remember)
memory = memory drop 1
memoryIndex = memory.length
next
}
}
def canRewind(n: Int) = memoryIndex - n >= 0
def rewind(n: Int) = {
require(memoryIndex - n >= 0, "Attempted to rewind past 'remember' limit")
memoryIndex -= n
this
}
def rewind() = {
memoryIndex = 0
this
}
override def hasNext = it.hasNext
}
示例使用:
List(1,2,2,3,3,3,4,5,5).iterator.groupWhen(_ == _).toList
给出:List(List(1), List(2, 2), List(3, 3, 3), List(4), List(5, 5))
如果要过滤掉单个元素,只需应用 afilter
或withFilter
之后groupWhen
Stream.continually(Random.nextInt(100)).iterator
.groupWhen(_ + _ == 100).withFilter(_.length > 1).take(3).toList
给出:List(List(34, 66), List(87, 13), List(97, 3))