我有多个迭代器,根据一些排序标准以已排序的方式返回项目。现在,我想将这些迭代器合并为一个组合迭代器(多路复用)。我知道如何使用 Java 风格实现,例如使用 tree-map,但我想知道是否有更多函数式的方法? 我希望尽可能保留这些迭代器的惰性特性。
您只需要执行以下操作:
val it = 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
}
但并不一定更快,您应该进行微基准测试。
一个可能的替代方案是使用缓冲迭代器来达到相同的效果。
DateTime
的形式存在。我想要这两个迭代器根据时间戳合并,而不是一个接一个(在Java中,我会使用比较器)。 - Bober02mergeStreams
方法进行折叠。不过,使用自定义的 Iterator
实现可能会更加高效。 - axel22就像@axel22提到的那样,您可以使用BufferedIterators来完成此操作。以下是一种不需要Stream的解决方案:
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!")
}
}
您可以尝试以下方法:
(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 // 结果为:1 merged.next // 结果为:2 merged.next // 结果为:3