2012-04-10 13 views
16

bardzo podoba mi się budowąScala - Iterator na wszystkich liniach w plikach w katalogu

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

dla iteracji nad plikiem w Scala i zastanawiam się, czy istnieje sposób, aby wykorzystać konstrukcję podobną do iteracji przez linie we wszystkich plikach w katalogu.

Istotnym ograniczeniem jest tutaj to, że wszystkie pliki sumują się do ilości miejsca, która spowodowałaby przepełnienie sterty. (myślę, że kilkadziesiąt GB, więc zwiększanie wielkości sterty nie jest opcją). Jak na razie pracuję nad tym, próbowałem każdego razem w jeden plik i używając powyższej konstrukcji, która działa b/c lenistwa.

Sytuacja, która wydaje się rodzić pytania typu .. czy mogę połączyć dwa (sto) leniwych iteratorów i uzyskać naprawdę duży, naprawdę leniwy?

Odpowiedz

27

Tak, chociaż to nie jest aż tak zwięzły:

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 } 

Sztuką jest flatMap i its for-comprehension syntactic sugar. Powyższy, na przykład, jest bardziej lub mniej równoważne do następujących:

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

Daniel Sobral zauważa w komentarzu poniżej, takie podejście (a kod w swoim pytaniu) pozostawi otwarte pliki. Jeśli jest to jednorazowy skrypt lub po prostu pracujesz w REPL, może to nie być wielka sprawa. Jeśli napotkasz problemy, można użyć pimp-my-library pattern wdrożyć podstawowe zarządzanie zasobami:

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 
    } 
    } 
} 

Teraz wystarczy użyć Source fromFile file getLinesAndClose i nie będzie musiał martwić się o pliki pozostają otwarte.

+0

To jest idealne, właśnie pobrałem około 10 gb plików przy użyciu repliki scala z bitem kodu opartym na tym, a użycie pamięci ledwo zniesione. Dzięki wielkie! –

+1

Należy jednak zauważyć, że 'Source' dla każdego pliku nie jest zamykany. W tym konkretnym przypadku, gdy kod może dotykać setek plików, ważne jest użycie pewnego rodzaju ARM. –