2012-07-03 4 views
7

So habe ich etwa eine 8 MB-Datei von jeder mit 6 INTS durch ein Leerzeichen getrennt.Haskell Effizientere Art, Datei von Ziffernzeilen zu analysieren

meine aktuelle Methode, dies für die Analyse ist:

tuplify6 :: [a] -> (a, a, a, a, a, a) 
tuplify6 [l, m, n, o, p, q] = (l, m, n, o, p, q) 

toInts :: String -> (Int, Int, Int, Int, Int, Int) 
toInts line = 
     tuplify6 $ map read stringNumbers 
     where stringNumbers = split " " line 

und Kartierung toInts über

liftM lines . readFile 

die mich wird eine Liste von Tupeln. Wenn ich dies jedoch ausführe, dauert es fast 25 Sekunden, um die Datei zu laden und zu parsen. Irgendwie kann ich das beschleunigen? Die Datei ist nur einfacher Text.

+0

könnten Sie ein bisschen mehr Informationen zur Verfügung stellen: das gesamte Arbeitsprogramm, Eingabe, wie Sie es ausführen, kompilieren Sie es (mit Optimierung) oder führen Sie es in 'ghci'. Kennst du 'Data.Betestring' und' Data.Vector'? Auch "lesen" ist ziemlich langsam, zumindest habe ich das gehört. – epsilonhalbe

+0

Siehe auch http://stackoverflow.com/questions/8366093/how-do-i-parse-a-matrix-of-integers-in-haskell/8366642 –

Antwort

8

Sie können es beschleunigen, indem Sie ByteString s, z.

module Main (main) where 

import System.Environment (getArgs) 
import qualified Data.ByteString.Lazy.Char8 as C 
import Data.Char 

main :: IO() 
main = do 
    args <- getArgs 
    mapM_ doFile args 

doFile :: FilePath -> IO() 
doFile file = do 
    bs <- C.readFile file 
    let tups = buildTups 0 [] $ C.dropWhile (not . isDigit) bs 
    print (length tups) 

buildTups :: Int -> [Int] -> C.ByteString -> [(Int,Int,Int,Int,Int,Int)] 
buildTups 6 acc bs = tuplify6 acc : buildTups 0 [] bs 
buildTups k acc bs 
    | C.null bs = if k == 0 then [] else error ("Bad file format " ++ show k) 
    | otherwise = case C.readInt bs of 
        Just (i,rm) -> buildTups (k+1) (i:acc) $ C.dropWhile (not . isDigit) rm 
        Nothing -> error ("No Int found: " ++ show (C.take 100 bs)) 

tuplify6:: [a] -> (a, a, a, a, a, a) 
tuplify6 [l, m, n, o, p, q] = (l, m, n, o, p, q) 

läuft ziemlich schnell:

$ time ./fileParse IntList 
200000 

real 0m0.119s 
user 0m0.115s 
sys  0m0.003s 

für eine 8,1 MiB-Datei.

Auf der anderen Seite, mit String s und Ihre Conversion (mit ein paar seq s Auswertung zu erzwingen) nahm auch nur 0.66s, so dass der Großteil der Zeit nicht zu sein scheint verbrachte Parsen, aber mit der Arbeit Ergebnis.

Hoppla, verpasst ein seq so die read s wurden nicht tatsächlich für die String Version ausgewertet. Festsetzung, dass String + read etwa vier Sekunden dauert, ein wenig über einen mit dem benutzerdefinierten Int Parser von @ Rotsor Kommentar

foldl' (\a c -> 10*a + fromEnum c - fromEnum '0') 0 

so Parsen eine erhebliche Menge der Zeit in Anspruch nehmen anscheinend tat.

+0

Danke. Ich habe die Haskell Lazy-Bewertung vergessen, also habe ich mich geirrt, wo das Timing-Problem herkommt. Aber danke für die andere Methode auch! – DantheMan

+0

Kannst du bitte das ganze Programm zeigen, das 0.66s mit 'read' erreicht? Ich habe [eine ähnliche Frage gestellt] (http://stackoverflow.com/questions/7510078/why-is-char-based-input-so-much-slower-than-the-char-based-output-in- haskell) vor und die Antwort war "lesen ist langsam". Hier ergibt das bloße Ersetzen von "Lesen" durch "falten" (\ a c -> a * 10 + von Enum c - von Enum '0') 0 eine 6-fache Verbesserung der Geschwindigkeit, was zeigt, dass die meiste Zeit tatsächlich durch Parsing genommen wurde. Wie hast du es geschafft, das zu verbessern? – Rotsor

Verwandte Themen