2017-06-14 7 views
0

Je dispose d'un fichier source qui contient des mots et que vous voulez faire le nombre typique de mot, je me sers quelque chose qui se transforme en tableau et prend en mémoireEst-ce que les mots comptent en prenant les entrées du fichier ligne par ligne dans Scala?

def freqMap(lines: Iterator[String]): Map[String, Int] = { 

    val mappedWords: Array[(String, Int)] = lines.toArray.flatMap((l: String) => l.split(delimiter).map((word: String) => (word, 1))) 

    val frequencies = mappedWords.groupBy((e) => e._1).map { case (key, elements) => elements.reduce((x, y) => (y._1, x._2 + y._2)) } 

    frequencies 
} 

Mais je veux évaluer ligne par ligne et montrer la sortie comme chaque la ligne est traitée. Comment cela peut-il être fait paresseusement et sans mettre tout en mémoire

Répondre

1

Vous dites que vous ne voulez pas tout mettre en mémoire, mais que vous voulez "afficher la sortie au fur et à mesure que chaque ligne est traitée". Cela sonne comme si vous voulez juste les résultats intermédiaires.

lines.foldLeft(Map[String,Int]()){ case (mp,line) => 
    println(mp) // output intermediate results 
    line.split(" ").foldLeft(mp){ case (m,word) => 
     m.lift(word).fold(m + (word -> 1))(c => m + (word -> (c+1))) 
    } 
} 

L'itérateur (lines) est consommé une à la fois. Le résultat Map est construit mot par mot et reporté ligne par ligne en tant qu'accumulateur foldLeft.

+0

Pour un fichier volumineux, l'efficacité serait-elle compromise si je comprends bien que les résultats de la carte sont construits mot par mot? – jdk2588

+0

Je ne suis pas certain de comprendre votre question. Le résultat 'Map' est construit mot à mot, mais comment peut-il être fait autrement? Même si vous deviez charger le fichier entier en mémoire (non recommandé pour un très gros fichier), diviser sur les espaces, puis 'groupBy (identité)', cela construit toujours le résultat un mot à la fois. Cela cache juste quelques détails. J'ai mentionné la procédure mot à mot pour marquer la distinction entre le traitement ligne par ligne dans lequel les résultats intermédiaires sont indiqués. Si quelque chose d'efficacité pourrait être améliorée car vous ne chargez pas tout en mémoire. – jwvh

0

Je pense que ce que vous cherchez sont la méthode scanLeft. Ainsi, par exemple la solution pourrait ressembler à ceci:

val iter = List("this is line number one", "this is line number two", "this this this").toIterator 

    val solution = iter.flatMap(_.split(" ")).scanLeft[Map[String, Int]](Map.empty){ 
    case (acc, word) => 
     println(word) 
     acc.updated(word, acc.getOrElse(word, 0) + 1) 
    } 

Il est tout paresseux et tirez base, si vous exécutez solution val = iter.flatMap scanLeftMap [String, Int] { cas (_ de split (» «).). (acc, mot) => println (mot) acc.updated (mot, acc.getOrElse (mot, 0) + 1) }

println(solution.take(3).toList) cela s'imprimé à la console:

val solution = iter.flatMap(_.split(" ")).scanLeft[Map[String, Int]](Map.empty){ 
case (acc, word) => 
    println(word) 
    acc.updated(word, acc.getOrElse(word, 0) + 1) 

}

this 
is 
line 
number 
one 
List(Map(), Map(this -> 1), Map(this -> 1, is -> 1), Map(this -> 1, is -> 1, line -> 1), Map(this -> 1, is -> 1, line -> 1, number -> 1)) 
+0

Dans scanLeft, la carte renvoie-t-elle une nouvelle carte à chaque fois? – jdk2588

+0

Ce que vous obtenez est un itérateur paresseux de Map [String, Int]. Il commence par un élément zéro, une carte vide, puis il a des états suivants. Donc, c'est comme un foldLeft, qui stocke tous les états intermédiaires. – ssn