2010-12-20 12 views
7

Ich bin auf der Suche nach einer effizienten Art und Weise Zahlen aus einer Textdatei zu lesen ohne zusätzliche Pakete zu installieren. Data.ByteString.Lazy.Char8.readInt scheint den Trick für ganze Zahlen zu machen. Ich habe gelesen, dass ByteString jetzt eine readDouble Methode, aber wenn ich schreibe import Data.ByteString.Lex.Lazy.Double (readDouble) der Compiler beschwert sich:Effiziente Zahl Lese in Haskell

 
    Main.hs:4:7: 
     Could not find module `Data.ByteString.Lex.Lazy.Double': 
      locations searched: 
      Data/ByteString/Lex/Lazy/Double.hs 
      Data/ByteString/Lex/Lazy/Double.lhs 

Mein bytestring Paketversion 0.9.1.5 ist.

Also, mache ich etwas falsch? Oder gibt es vielleicht eine bessere Lösung für das Problem? Vielen Dank.

Update: OK, scheint, dass readDouble in Paket bytestring-Lexer ist, die nicht standardmäßig installiert ist. Irgendeine andere Idee?

+1

gerade das dann bytestring-Lexer-Paket installieren. "cabal install bystring-lexer" – sclv

+1

Ich möchte auf zusätzliche Pakete verzichten, da meine Programme auf Servern laufen, über die ich keine Kontrolle habe. – adamax

+0

@adamax: Es lohnt sich, diese Einschränkung zu Ihrer Frage hinzuzufügen. –

Antwort

3

Das einzige Mal, dass ich auf dem kritischen Pfad Parsen verdoppelt begegnet, habe ich dies:

{-# LANGUAGE ForeignFunctionInterface #-} 
import qualified Data.ByteString.Char8 as B 
import Foreign.C.Types 
import Foreign.C.String 
import System.IO.Unsafe 

foreign import ccall unsafe "stdlib.h atof" c_atof :: CString -> IO CDouble 
unsafeReadDouble = unsafePerformIO . flip B.useAsCString c_atof 

Es war nichts, das zwar wie ein readDouble sah damals in bytestring. Das wäre wahrscheinlich eine bessere Lösung, wenn es jetzt Standard ist.

+0

Danke! Ich habe ein paar Experimente gemacht.Um es einfacher zu machen, habe ich Atoi anstelle von atof genommen und es mit der üblichen Showfunktion und meiner naiven Implementierung (iread) verglichen. FFI schlägt total Show, aber es verliert etwa 20% nach Iread. Möglicherweise entsteht durch die Konvertierung in CString ein Overhead – adamax

2

Hier ist, was ich kam mit.

ich verwendet, um die Funktion von JB angeboten und hat zwei Tricks, die ich aus dem Quellcode von bytestring-lexing gelernt (danke, SCLV!). Die erste ist diese Funktion:

strict = SB.concat . LB.toChunks

Es wandelt eine faule Bytestring effizient in non-fazy um.

Der zweite Trick ist Funktion Data.ByteString.Internal.inlinePerformIO, die eine effizientere Variante des unsafePerformIO ist.

Hier kompletter Code, der ein ziemlich schnell Zahl Lesen erlaubt:


{-# LANGUAGE ForeignFunctionInterface #-} 

import qualified Data.ByteString.Lazy.Char8 as LB 
import qualified Data.ByteString as SB 
import Data.ByteString.Internal (inlinePerformIO) 
import Foreign.C.String (CString) 
import Foreign.C (CDouble) 
import Data.Maybe (fromJust) 

foreign import ccall unsafe "stdlib.h atof" c_atof :: CString -> IO Double 
unsafeReadDouble = inlinePerformIO . flip SB.useAsCString c_atof 
{-# INLINE unsafeReadDouble #-} 
readDouble = unsafeReadDouble . SB.concat . LB.toChunks 
readInt = fst . fromJust . LB.readInt 

Und ein Beispielprogramm, das die Summe aller Zahlen in der Eingabe berechnet:

 
main = LB.getContents >>= (print . sum . map readDouble . LB.lines) 
It processes an 11Mb file (1M numbers) in about 0.5 seconds

I also found several links , where a much more efficient version of readInt diskutiert wird. Vermutlich kann man basierend auf ähnlichen Ideen eine readDouble bauen. Aber ich denke, ich bleibe jetzt bei meiner aktuellen Version.

5

Eine andere Lösung: Installieren Sie das bytestring-lexing Paket und verwenden Sie readDouble, die ich für Sie optimiert habe.

cabal install bytestring-lexing 

Das Paket bietet optimized parsing functions für Punkt Literale floating:

readDouble :: ByteString -> Maybe (Double, ByteString)