【问题标题】:Scala Seq.grouped eating my iteratorScala Seq.grouped 吃掉我的迭代器
【发布时间】:2012-10-24 10:34:22
【问题描述】:

作为一名 C# 程序员,我对 Java / Scala 迭代器设计有一个粗略的了解。

我正在尝试(懒惰地-因为来源可能很大)从RecordReader(在某些第三方库中)读取记录。每 100 条记录我需要做一些额外的工作。

for (group <- reader.iterator.zipWithIndex.grouped(100)) {
  for ((record, i) <- group) {
    println(i + "|" + record.key)
  }
  // ...
}

这给了我最后的记录,每次都重复。

如果我不使用grouped,它可以正常工作并且我会得到每条记录。我是否缺少有关惰性流或 Java 迭代器的内容?

【问题讨论】:

  • 问题出在您未显示的代码或第三方库的Iterator 实现中,因为我无法使用标准库中的类重现您的问题。
  • 谢谢。我的感觉是第三方迭代器的行为很糟糕——但问题是这是否真的如此,还是我误解了。
  • 令人惊讶的是,我写得很仔细、礼貌的问题被否决了。为 Scala 社区走的路。
  • 谁可以读心术
  • 一方面,没有人知道是什么让您认为您每次都获得相同的记录。你只是打印它们吗?如果是这样,那不应该发生。另外,你有 3rd 方库的来源吗?如果是,请发布.iterator 方法的实现。

标签: java scala iterator


【解决方案1】:

要进行故障排除,请尝试在另一个迭代器中装饰您的迭代器,以打印正在发生的事情:

def wrap[T](i: Iterator[T]) = new Iterator[T] {
  def hasNext = { val b = i.hasNext; println("hasNext => " + b); b }
  def next() = { val n = i.next(); println("next() => " + n); n }
}

val reader = Iterator.from(20).take(10).toList
for (group <- wrap(reader.iterator).zipWithIndex.grouped(5)) {
  for ((v, i) <- group) println("[" + i + "] = " + v)
}

在迭代器上调用 wrap,第一次实例化迭代器时。这将打印如下内容:

hasNext => true
hasNext => true
next() => 20
hasNext => true
next() => 21
hasNext => true

这应该可以帮助您确定迭代器是否表现不佳...例如,库无法正确处理多次调用 hasNext 而不调用 next。在这种情况下,您可以修改 wrap 以使迭代器正常运行。还有一件事,从症状来看,感觉就像您在调用分组之前已经使用了迭代器。所以要格外小心,检查你之前是否使用过相同的迭代器引用。

【讨论】:

    【解决方案2】:

    我认为问题可能在于Record.key 只是返回某个变量的当前值,该变量在迭代器被消耗时发生了变异 (而不是让记录在构建时实际捕获关键值)。 一个例子可能会让它更清楚。 首先,让我们使用 scala REPL 编写一些没有出现问题的测试代码:

    case class Record( key: Int )
    def getRecordIterator: Iterator[Record] = {
      var currentKey: Int = 0
      (1 to 10).iterator.map{ i => 
        currentKey += 1
        new Record( currentKey )
      }
    }
    

    那么我们可以尝试不使用grouped进行迭代:

    for ((record, i) <- getRecordIterator.zipWithIndex) {
      println(i + "|" + record)
    }
    

    这给了我们(如预期的那样)

    0|Record(1)
    1|Record(2)
    2|Record(3)
    3|Record(4)
    4|Record(5)
    5|Record(6)
    6|Record(7)
    7|Record(8)
    8|Record(9)
    9|Record(10)
    

    然后使用grouped

    for (group <- getRecordIterator.zipWithIndex.grouped(3)) {
      for ((record, i) <- group) {
        println(i + "|" + record)
      }
      println("---")
    }
    

    这给出了:

    0|Record(1)
    1|Record(2)
    2|Record(3)
    ---
    3|Record(4)
    4|Record(5)
    5|Record(6)
    ---
    6|Record(7)
    7|Record(8)
    8|Record(9)
    ---
    9|Record(10)
    ---    
    

    到目前为止,一切都很好。

    现在我们稍微改变一下Record的定义:

    trait Record {
      def key: Int
      override def toString = "Record(" + key + ")"
    }
    def getRecordIterator: Iterator[Record] = {
      var currentKey: Int = 0
      (1 to 10).iterator.map{ i => 
        currentKey += 1
        new Record{ def key = currentKey }
      }    
    }
    

    有了这个改变,我们在不使用grouped 时仍然得到相同的结果,但是当我们使用group 时得到的结果如下:

    0|Record(3)
    1|Record(3)
    2|Record(3)
    ---
    3|Record(6)
    4|Record(6)
    5|Record(6)
    ---
    6|Record(9)
    7|Record(9)
    8|Record(9)
    ---
    9|Record(10)
    ---
    

    问题的根源在于,仅仅在我们的迭代器上调用next 就改变了Record.get 返回的值。 这个问题可以更简单地说明:

    val it = getRecordIterator
    val r1 = it.next
    println(r1) // prints "Record(1)" as expected
    val r2 = it.next
    println(r2) // prints "Record(2)" as expected
    println(r1) // this now prints "Record(2)", not "Record(1)" anymore!
    

    【讨论】:

      猜你喜欢
      • 2015-05-05
      • 2017-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-20
      相关资源
      最近更新 更多