2011-07-21 10 views
7

昨日、このコードは私に頭痛を引き起こしました。ファイルを行ごとに読み込んで修正しました。何か案は ?ループ中のこのコードでは奇妙な文字列

whileループは、ファイル内の行の何がこれは確かにトリッキーである1より大きい

val lines = Source.fromFile(new File("file.txt")).getLines; 

println("total lines:"+lines.size); 

var starti = 1; 
while(starti < lines.size){ 
    val nexti = Math.min(starti + 10, lines.size ); 

    println("batch ("+starti+", "+nexti+") total:" + lines.size) 
    val linesSub = lines.slice(starti, nexti) 
    //do something with linesSub 
    starti = nexti 
} 

答えて

14

であっても実行されません取得すると思われることはありませんし、私もそれがIteratorのバグだと言うでしょう。 getLinesは遅れて進行するIteratorを返します。だから、あなたがlines.sizeを要求すると、イテレータはファイル全体を調べて、その行を数えることになります。その後、それは「疲れ」だ:

scala> val lines = io.Source.fromFile(new java.io.File("....txt")).getLines 
lines: Iterator[String] = non-empty iterator 

scala> lines.size 
res4: Int = 15 

scala> lines.size 
res5: Int = 0 

scala> lines.hasNext 
res6: Boolean = false 

あなたが見る、あなたが二回sizeを実行すると、結果がゼロです。

イテレータをlines.toSeqのような「安定した」ものにするかのどちらかの方法があります。それとも、およそsizeを忘れると「通常」の繰り返しを実行します。

while(lines.hasNext) { 
    val linesSub = lines.take(10) 
    println("batch:" + linesSub.size) 
    // do something with linesSub 
} 
+1

+1はScalaのwhileループで反復処理を行う方法を指摘しています。 –

+0

なぜこれはバグだと思いますか?反復子は通常、それらが含む要素の数を知らない。特に、ファイル全体を読み取らずにファイル内の行数を調べることはできません。 – ziggystar

+0

これはバグと呼ばれるものによって異なります。著者が意図したとおりに動作し、文書化されています。しかし、この命名は直感的ではありません。私は間違いなくイテレータを返すとは期待していませんでした。 getLinesはどのような方法でもイテレータを意味しません。私は間違いなくこの機能が好きです。 – smartnut007

4

それはlinesはイテレータ、配列ではありませんので、これは0を返すlines.sizeを呼び出す二度目。私はそれを@に提案された、Seq方法でコードを書き換えました

4

は答える0__:上記の回答の

val batchSize = 10; 
val lines = Source.fromFile("file.txt").getLines.toSeq; 

println("total lines:"+lines.length); 

var processed = 0; 
lines.grouped(batchSize).foreach(batch => { 
     println("batch ("+processed+","+(processed+Math.min(lines.length-processed,batchSize))+") 
       total:"+lines.length 
    ); 
     processed = processed + batchSize; 
     //do something with batch 
    } 
) 
5

なしは非常に頭の上に釘を打つん。

ここにIteratorが返される理由はありますか?怠け者であれば、ヒープを押さえることができます。そして、各行を表すStringは、処理が終了するとすぐにガベージコレクションされます。大きなファイルの場合、これはOutOfMemoryExceptionを回避するためにすべての違いを生むことができます。

理想的には、イテレータを直接操作して厳密なコレクション型にするのは理想的です。

for (linesSub <- lines grouped 10) { 
    //do something with linesSub 
} 

をそして、あなたはprintlnカウンタを保持したい場合は、インデックスにジップ:

OM-NOM-NOMの答えに従って、そしてgroupedを使用して、本当に

for ((linesSub, batchIdx) <- (lines grouped 10).zipWithIndex) { 
    println("batch " + batchIdx) 
    //do something with linesSub 
} 

あなたの場合は合計を必要とし、getLinesを2回呼び出します。カウントのために1回、実際にラインを処理する2回目。