3

C#プログラマーとして、私はJava/Scalaイテレーターの設計について大ざっぱな理解を持っています。

私は(怠惰に-ソースが大きいかもしれないので)RecordReader(いくつかのサードパーティのライブラリで)からレコードを読み取ろうとしています。100レコードごとに追加の作業を行う必要があります。

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

これにより、毎回、最後のレコードが繰り返し表示されます。

を使用しない場合は正常にgrouped動作し、各レコードを取得します。レイジーストリーミングまたはJavaイテレータについて何かが足りませんか?

4

2 に答える 2

3

トラブルシューティングを行うには、何が起こっているかを出力する別のイテレータでイテレータを装飾してみてください。

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、イテレータが正しく動作するように変更できます。もう1つ、症状から、グループ化が呼び出される前に、イテレータをすでに消費しているように感じます。したがって、特に注意して、以前に同じイテレータ参照を使用したことがあるかどうかを確認してください。

于 2012-10-24T14:05:34.557 に答える
3

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!
于 2012-10-24T14:15:11.340 に答える