如何合并迭代器解析器

时间:2016-04-14 12:35:21

标签: scala iterator

我有一个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}}应该是首选。

2 个答案:

答案 0 :(得分:3)

没有。您的假设函数必须首先调用fAfB中的一个。假设它调用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,因此如果fAfB生成不均匀速率的元素,则可能会出现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

所以,总的来说这是一个相当好的和简洁的解决方案,除非你有一些极端的用例。