2010-09-13 6 views
2

J'ai un fichier qui ressemble à ce index : label, indice de valeur de contiennent des clés de la gamme de 0... 100000000 et étiquette peut être une valeur String, je veux partager ce fichier qui a 110 Mo en plusieurs tranches de 100 lignes chacune et fait un calcul sur chaque tranche. Comment puis-je faire ceci?Comment diviser un fichier 110Mo avec Haskell

123 : "acgbdv" 

127 : "ytehdh" 

129 : "yhdhgdt" 

... 

9898657 : "bdggdggd" 
+3

Qu'est-ce qu'un "Mo"? – jrockway

+0

millions d'objets? – kennytm

+2

C'est l'équivalent français de "MB" - méga-octet. – Chuck

Répondre

3

Si vous utilisez chaîne IO, vous pouvez effectuer les opérations suivantes:

import System.IO 
import Control.Monad 

-- | Process 100 lines 
process100 :: [String] -> MyData 
-- whatever this function does 

loop :: [String] -> [MyData] 
loop lns = go [] lns 
    where 
    go acc [] = reverse acc 
    go acc lns = let (this, next) = splitAt 100 lns in go (process100 this:acc) next 

processFile :: FilePath -> IO [MyData] 
processFile f = withFile f ReadMode (fmap (loop . lines) . hGetContents) 

Notez que cette fonction traitera silencieusement le dernier morceau, même si ce ne sont pas exactement 100 lignes.

paquets comme bytestring et texte fournissent généralement des fonctions comme lines et hGetContents donc vous devriez être en mesure d'adapter facilement cette fonction à l'un d'eux.

Il est important de savoir ce que vous faites avec les résultats du traitement de chaque tranche, car vous ne voulez pas conserver ces données plus longtemps que nécessaire. Idéalement, après avoir calculé chaque tranche, les données seraient entièrement consommées et pourraient être gcées. Généralement, soit les résultats séparés sont combinés en une structure de données unique (un "pli"), soit chacun est traité séparément (peut-être en sortie une ligne vers un fichier ou quelque chose de similaire). Si c'est un pli, vous devriez changer « boucle » pour ressembler à ceci:

loopFold :: [String] -> MyData -- assuming there is a Monoid instance for MyData 
loopFold lns = go mzero lns 
    where 
    go !acc [] = acc 
    go !acc lns = let (this, next) = splitAt 100 lns in go (process100 this `mappend` acc) next 

La fonction loopFold utilise des modèles bang (activé avec « LANGUE BangPatterns » pragma) pour forcer l'évaluation de la « MyData ». En fonction de ce que MyData est, vous devrez peut-être utiliser deepseq pour vous assurer qu'il est entièrement évalué.

Si vous écrivez au lieu chaque ligne de sortie, laisser loop tel qu'il est et changer processFile:

processFileMapping :: FilePath -> IO() 
processFileMapping f = withFile f ReadMode pf 
    where 
    pf = mapM_ (putStrLn . show) <=< fmap (loop . lines) . hGetContents 

Si vous êtes intéressé par le traitement de style recenseur/iteratee, cela est un problème assez simple. Je ne peux pas donner un bon exemple sans savoir quel type de travail fait process100, mais cela impliquerait enumLines et take.

Est-il nécessaire de traiter exactement 100 lignes à la fois, ou voulez-vous juste traiter en morceaux pour plus d'efficacité? Si c'est le dernier, ne vous inquiétez pas à ce sujet. Il vaut probablement mieux traiter une ligne à la fois, en utilisant une fonction de pli réelle ou une fonction similaire à processFileMapping.