18

私は本当に好きです

for (line <- Source fromFile inputPath getLines) {doSomething line}

Scala内のファイルを反復処理するための構造であり、ディレクトリ内のすべてのファイルの行を反復処理するために同様の構造を使用する方法があるかどうか疑問に思っています。

ここでの重要な制限は、すべてのファイルが合計してヒープオーバーフローを生成するスペースになることです。(数十GBと考えるので、ヒープサイズを増やすことはできません)当面の回避策として、私はすべてを1つのファイルにまとめ、上記の構造を使用して怠惰のb/cを実行しました。

要するに、これは次のような質問を提起するようです.. 2つの(100)怠惰なイテレータを連結して、本当に大きくて本当に怠惰なイテレータを取得できますか?

4

1 に答える 1

27

はい、それほど簡潔ではありませんが、次のようになります。

import java.io.File
import scala.io.Source

for {
  file <- new File(dir).listFiles.toIterator if file.isFile
  line <- Source fromFile file getLines
} { doSomething line }

秘訣はflatMapその理解のfor構文糖衣です。たとえば、上記は多かれ少なかれ次と同等です。

new File(dir)
  .listFiles.toIterator
  .filter(_.isFile)
  .flatMap(Source fromFile _ getLines)
  .map(doSomething)

Daniel Sobralが以下のコメントで述べているように、このアプローチ(およびあなたの質問のコード)はファイルを開いたままにします。これが1回限りのスクリプトである場合、またはREPLで作業しているだけの場合、これは大したことではないかもしれません。問題が発生した場合は、pimp-my-libraryパターンを使用して、基本的なリソース管理を実装できます。

implicit def toClosingSource(source: Source) = new {
  val lines = source.getLines
  var stillOpen = true
  def getLinesAndClose = new Iterator[String] {
    def hasNext = stillOpen && lines.hasNext
    def next = {
      val line = lines.next
      if (!lines.hasNext) { source.close() ; stillOpen = false }
      line
    }
  }
}

今すぐ使用Source fromFile file getLinesAndCloseすれば、ファイルが開いたままになることを心配する必要はありません。

于 2012-04-10T22:28:39.000 に答える