我有一个Iterator[(A1,B1)]
和两个函数
fA: (Iterator[A1]) => Iterator[A2]
和fB: (Iterator[B1]) => Iterator[B2]
。 是否可以在不将迭代器转换为Seq的情况下生成fAB: (Iterator[(A1,B1)]) => Iterator[(A2,B2)]
以下两个答案都很好。我选择@Aivean's answer因为代码更简单并且它使用专门的scala数据结构(Stream)。
唯一的缺点是stackoverfow限制,但对于大多数用例来说它应该不是问题。如果你的迭代器非常(非常)长,那么{/ 3}}应该是首选。
答案 0 :(得分:3)
没有。您的假设函数必须首先调用fA
和fB
中的一个。假设它调用fA
并在生成任何内容之前请求所有A1
s。然后你没有剩余的B1
传递给fB
,除非你把它们保存在某个地方,可能会泄漏内存。如果这是可以接受的,你可以这样做:
def unzip[A, B](iter: Iterator[(A, B)]) = {
var qA = Queue.empty[A]
var qB = Queue.empty[B]
val iterA = new Iterator[A] {
override def hasNext = qA.nonEmpty || iter.hasNext
override def next() = qA.dequeueOption match {
case Some((a, qA1)) =>
qA = qA1
a
case None =>
val (a, b) = iter.next()
qB = qB.enqueue(b)
a
}
}
// similar iterB
(iterA, iterB)
}
然后
val (iterA, iterB) = unzip(iterator)
fA(iterAfA).zip(fB(iterB))
(好吧,你也可以写iterator => fA(iterator.map(_._1)).zip(fB(iterator.map(_._2))
:它有正确的类型,但可能不是你想要的。也就是说,它只使用原始迭代器产生的每个元组的一个字段,然后删除其他。)
答案 1 :(得分:2)
我实现起来更简单:
def iterUnzip[A1, B1, A2, B2](it: Iterator[(A1, B1)],
fA: (Iterator[A1]) => Iterator[A2],
fB: (Iterator[B1]) => Iterator[B2]) =
it.toStream match {
case s => fA(s.map(_._1).toIterator).zip(fB(s.map(_._2).toIterator))
}
我们的想法是将迭代器转换为流。 Scala中的Stream
是懒惰的,但也提供memoization。这有效地提供了与@ AlexeyRomanov解决方案相同的缓冲机制,但更简洁。唯一的缺点是Stream
在堆栈上存储了memoized元素而不是显式Queue,因此如果fA
和fB
生成不均匀速率的元素,则可能会出现StackOverflow异常。
测试评估确实是懒惰的:
val iter = Stream.from(0).map(x => (x, x + 1))
.map(x => {println("fetched: " + x); x}).take(5).toIterator
iterUnzip(
iter,
(_:Iterator[Int]).flatMap(x => List(x, x)),
(_:Iterator[Int]).map(_ + 1)
).toList
结果:
fetched: (0,1)
iter: Iterator[(Int, Int)] = non-empty iterator
fetched: (1,2)
fetched: (2,3)
fetched: (3,4)
fetched: (4,5)
res0: List[(Int, Int)] = List((0,2), (0,3), (1,4), (1,5), (2,6))
我也很难通过生成不均匀的迭代器来获得StackOverflow异常,但是失败了。
val iter = Stream.from(0).map(x => (x, x + 1)).take(10000000).toIterator
iterUnzip(
iter,
(_:Iterator[Int]).flatMap(x => List.fill(1000000)(x)),
(_:Iterator[Int]).map(_ + 1)
).size
在-Xss5m
上正常工作并产生:
res10: Int = 10000000
所以,总的来说这是一个相当好的和简洁的解决方案,除非你有一些极端的用例。