2010-06-10 5 views
24

Was ist das "richtige" Schreiben der standard Read-While-Schleife in Scala? Mit richtig meine ich geschrieben in einer Scala-ähnlichen Weise im Gegensatz zu einer Java-ähnlichen Art. HierWas ist der richtige Weg, um eine Read-While-Schleife in Scala zu programmieren?

ist der Code, den ich in Java haben:

MessageDigest md = MessageDigest.getInstance("MD5"); 
InputStream input = new FileInputStream("file"); 
byte[] buffer = new byte[1024]; 
int readLen; 
while((readLen = input.read(buffer)) != -1) 
    md.update(buffer, 0, readLen); 
return md.digest(); 

Hier ist der Code, den ich in Scala haben:

val md = MessageDigest.getInstance(hashInfo.algorithm) 
val input = new FileInputStream("file") 
val buffer = new Array[ Byte ](1024) 
var readLen = 0 
while(readLen != -1) 
{ 
    readLen = input.read(buffer) 
    if(readLen != -1) 
     md.update(buffer, 0, readLen) 
} 
md.digest 

Der Scala-Code korrekt ist und funktioniert, aber fühlt sich sehr un-Scala -ich. Zum einen ist es eine wörtliche Übersetzung des Java-Codes, der keinen der Vorteile von Scala nutzt. Weiter ist es tatsächlich länger als der Java-Code! Ich habe wirklich das Gefühl, dass ich etwas verpasse, aber ich kann nicht herausfinden, was.

Ich bin ziemlich neu in Scala, und deshalb stelle ich die Frage, um nicht in die Falle zu geraten, Java-Code in Scala zu schreiben. Ich interessiere mich mehr für den Scala-Weg, um diese Art von Problem zu lösen, als in irgendeiner speziellen Hilfsmethode, die von der Scala-API bereitgestellt wird, um eine Datei zu hashen.

(ich im Voraus entschuldigen für meine Ad-hoc-Scala Adjektive in dieser Frage.)

+4

Meine Antwort zu http://stackoverflow.com/questions/2849303 kann hilfreich sein. –

+5

@Rex Ich würde "Iterator" anstelle von "Stream" verwenden. Schließlich ist es ein One-Shot und nicht wiederverwendbar. Außerdem hat 'Iterator' bei solchen Aufgaben eine bessere Speicherleistung. –

+0

@Daniel - Einverstanden. Ich glaube, dass es an einem Punkt einen guten Grund gab, warum ich 'Stream' vorher benutzt habe, aber ich kann mich nicht mehr erinnern, was (und ich denke nicht, dass es trotzdem wahr bleibt). Unabhängig davon, hier 'Iterator.continually' sollte in Ordnung sein. –

Antwort

25

Basierend auf Rex Post, die er erwähnt:

Stream.continually(input.read(buffer)).takeWhile(_ != -1).foreach(md.update(buffer, 0, _)) 

Sie sollten die var readLen +, während {ersetzen .. .} Zeilen damit erzeugt es das gleiche Ergebnis.

Wie Rex erwähnt, funktioniert es mit scala 2.8.

+2

Wenn Stream Ihnen die Willies gibt, können Sie auch einfach Iterator.continual verwenden. –

+0

Wenn ich es in einem InputStream aus einem Process heraus versuche, wird die foreach-Methode nur für das erste Zeichen aufgerufen und dann gestoppt. Wenn mit while verwendet, bekomme ich alle Daten. Irgendeine Idee warum? – pommedeterresautee

+0

Wie bekomme ich die Anzahl der gelesenen Bytes von "input.read (...)" um sie in "foreach" zu verwenden? –

7

Was Rex Kerr in seinem Kommentar schlägt vor, ist die folgende:

val md = MessageDigest.getInstance("MD5") 
val input = new FileInputStream("foo.txt") 
val buffer = new Array[ Byte ](1024) 
Stream.continually(input.read(buffer)) 
    .takeWhile(_ != -1) 
    .foreach(md.update(buffer, 0, _)) 
md.digest 

Der Schlüssel ist die Stream.continually. Es erhält einen Ausdruck, der kontinuierlich ausgewertet wird, wodurch ein unendlicher Wert des ausgewerteten Ausdrucks erzeugt wird. Die takeWhile ist die Übersetzung aus dem while -Zustand. Die foreach ist der Körper der while -loop.

0

Wie wäre es mit einer Curry-Funktion? Sie bis zu 11 Zeilen Scala Code werden:

val md = MessageDigest.getInstance(hashInfo.algorithm) 
val input = new FileInputStream("file") 
iterateStream(input){ (data, length) => 
    md.update(data, 0, length) 
} 
md.digest 

Die iterateStream Funktion auf der Linie 3, die Sie zu einer Bibliothek hinzufügen könnten, ist:

def iterateStream(input: InputStream)(f: (Array[Byte], Int) => Unit){ 
    val buffer = new Array[Byte](512) 
    var curr = input.read(buffer) 
    while(curr != -1){ 
     f(buffer, curr) 
     curr = input.read(buffer) 
    } 
} 

Der hässliche duplizierten Code Enden (wo der Eingang gelesen wird) in der Bibliothek, gut getestet und versteckt vom Programmierer. Ich finde, dass der erste Codeblock weniger komplex ist als die Iterator.continually Lösung.

Verwandte Themen