2010-06-10 4 views
24

Quel est le "bon" d'écrire la boucle de lecture standard dans Scala? Par vrai, je veux dire écrit d'une manière semblable à Scala, par opposition à une manière similaire à Java.Quelle est la bonne façon de coder une boucle read-while dans Scala?

Voici le code que j'ai en Java:

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(); 

Voici le code que j'ai à Scala:

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 

Le code Scala est correct et fonctionne, mais se sent très non Scala -ish. Pour l'un, c'est une traduction littérale du code Java, ne tirant parti d'aucun des avantages de Scala. En outre, il est en fait plus long que le code Java! J'ai vraiment l'impression qu'il me manque quelque chose, mais je n'arrive pas à comprendre quoi. Je suis assez nouveau à Scala, et donc je pose la question pour éviter de tomber dans le piège de l'écriture de code Java dans Scala. Je suis plus intéressé par la méthode Scala pour résoudre ce genre de problème que dans n'importe quelle méthode d'assistance spécifique qui pourrait être fournie par l'API Scala pour hacher un fichier.

(je présente mes excuses à l'avance pour mes ad hoc adjectifs Scala dans toute cette question.)

+4

Ma réponse à http://stackoverflow.com/questions/2849303 peut être utile. –

+5

@Rex J'utiliserais 'Iterator' au lieu de' Stream'. Après tout, c'est un one-shot et non réutilisable. En outre, 'Iterator' a de meilleures performances de mémoire dans de telles tâches. –

+0

@Daniel - D'accord. Je crois qu'il y avait une bonne raison à un moment donné d'utiliser Stream avant, mais je ne me souviens plus de quoi (et je ne pense pas que cela reste vrai de toute façon). Peu importe, ici 'Iterator.continually' devrait être bien. –

Répondre

25

Basé sur le poste de Rex qu'il a mentionné:

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

Vous devez remplacer le var readLen + tandis que {.. .} avec lui, il produit le même résultat.

Comme Rex mentionné, il fonctionne avec scala 2.8.

+2

Si Stream vous donne les willies, vous pouvez aussi simplement utiliser Iterator.continually. –

+0

Lorsque je l'essaye sur un InputStream que j'ai obtenu à partir d'un Processus, la méthode foreach est juste appelée pour le premier caractère, puis s'arrête. Quand je l'utilise avec, j'obtiens toutes les données. Une idée de pourquoi? – pommedeterresautee

+0

Comment puis-je obtenir la quantité d'octets lus dans "input.read (...)" pour l'utiliser dans "foreach"? –

7

Ce que Rex Kerr suggère dans son commentaire est le suivant:

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 

La clé est le Stream.continually. Il obtient une expression qui est évaluée continuellement, en créant un Stream infini de l'expression évaluée. La takeWhile est la traduction de la condition while. Le foreach est le corps de la boucle while.

0

Qu'en est-il d'une fonction au curry? Vous 11 lignes de code Scala deviennent:

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

La fonction iterateStream sur la ligne 3, vous pouvez ajouter à une bibliothèque est:

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

Le code dupliquée laid (où l'entrée est lu) extrémités dans la bibliothèque, bien testé et caché loin du programmeur. Je pense que le premier bloc de code est moins complexe que la solution Iterator.continually.

Questions connexes