我有多个迭代器,它们根据某些排序标准以排序的方式返回项目。现在,我想将迭代器合并(多路复用)到一个组合迭代器中。我知道如何用Java风格来做,例如树形图,但我想知道是否有更多功能方法?我想尽可能地保留迭代器的懒惰。
答案 0 :(得分:36)
你可以这样做:
val it = iter1 ++ iter2
它创建另一个迭代器并且不评估元素,但包装两个现有的迭代器。
这完全是懒惰的,所以一旦你这样做,就不应该使用iter1
或iter2
。
通常,如果要合并更多迭代器,可以使用折叠:
val iterators: Seq[Iterator[T]] = ???
val it = iterators.foldLeft(Iterator[T]())(_ ++ _)
如果您对要生成的迭代器中要维护的元素有一些排序但是您想要延迟,则可以将它们转换为流:
def merge[T: Ordering](iter1: Iterator[T], iter2: Iterator[T]): Iterator[T] = {
val s1 = iter1.toStream
val s2 = iter2.toStream
def mergeStreams(s1: Stream[T], s2: Stream[T]): Stream[T] = {
if (s1.isEmpty) s2
else if (s2.isEmpty) s1
else if (s1.head < s2.head) s1.head #:: mergeStreams(s1.tail, s2)
else s2.head #:: mergeStreams(s1, s2.tail)
}
mergeStreams(s1, s2).iterator
}
虽然不一定快,但你应该对此进行微基准测试。
可能的替代方法是使用buffered iterators来达到同样的效果。
答案 1 :(得分:4)
与@ axel22一样,您可以使用BufferedIterators执行此操作。这是一个无流解决方案:
def combine[T](rawIterators: List[Iterator[T]])(implicit cmp: Ordering[T]): Iterator[T] = {
new Iterator[T] {
private val iterators: List[BufferedIterator[T]] = rawIterators.map(_.buffered)
def hasNext: Boolean = iterators.exists(_.hasNext)
def next(): T = if (hasNext) {
iterators.filter(_.hasNext).map(x => (x.head, x)).minBy(_._1)(cmp)._2.next()
} else {
throw new UnsupportedOperationException("Cannot call next on an exhausted iterator!")
}
}
答案 2 :(得分:3)
你可以尝试:
(iterA ++ iterB).toStream.sorted.toIterator
例如:
val i1 = (1 to 100 by 3).toIterator val i2 = (2 to 100 by 3).toIterator val i3 = (3 to 100 by 3).toIterator val merged = (i1 ++ i2 ++ i3).toStream.sorted.toIterator merged.next // results in: 1 merged.next // results in: 2 merged.next // results in: 3