2010-12-20 15 views
7

Poszukuję skutecznego sposobu na odczytanie liczb z pliku tekstowego bez instalowania dodatkowych pakietów. Data.ByteString.Lazy.Char8.readInt wydaje się robić lewę dla liczb całkowitych. Czytałem, że ByteString ma teraz metodę readDouble, ale kiedy piszę import Data.ByteString.Lex.Lazy.Double (readDouble) kompilator narzeka:Efektywne czytanie liczb w 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 

Moja wersja pakietu bytestring jest 0.9.1.5.

Czy robię coś nie tak? A może jest lepsze rozwiązanie tego problemu? Dzięki.

Aktualizacja: OK, wydaje się, że readDouble jest w pakiecie bytestring-lexer, który nie jest domyślnie instalowany. Każdy inny pomysł?

+1

wystarczy zainstalować pakiet bytestring-Lexer wtedy. "cabal install bytestring-lexer" – sclv

+1

Chcę obejść się bez dodatkowych pakietów, ponieważ moje programy będą działały na serwerach, nad którymi nie mam kontroli. – adamax

+0

@adamax: Warto dodać to ograniczenie do twojego pytania. –

Odpowiedz

3

Tylko raz natknąłem parsowania debla na ścieżce krytycznej, użyłem to:

{-# 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 

Nie było niczego, co wyglądało jak readDouble w bytestring w tym czasie, choć. Prawdopodobnie byłoby to lepsze rozwiązanie, gdyby było standardowe.

+0

Dzięki! Zrobiłem kilka eksperymentów.Żeby było łatwiej, wziąłem atoi zamiast atofa i porównałem go ze zwykłą funkcją show i moją naiwną implementacją (iread). FFI totalnie bije show, jednak traci około 20% na Iread. Być może narzut spowodowany konwersjami do CString – adamax

2

Oto, co wymyśliłem.

Użyłem funkcji oferowanej przez JB i dodałem dwie sztuczki, których nauczyłem się z kodu źródłowego bytestring-lexing (thanks, sclv!). Pierwsza to ta funkcja:

strict = SB.concat . LB.toChunks

Skutecznie przekształca leniwy byt w spokojny.

Druga sztuczka to funkcja Data.ByteString.Internal.inlinePerformIO, która jest bardziej wydajnym wariantem unsafePerformIO.

Oto kompletny kod, który pozwala na dość szybki odczyt numeru:


{-# 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 

I przykładowy program, który oblicza sumę wszystkich liczb na wejściu:

 
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 jest omawiany. Prawdopodobnie można zbudować readDouble w oparciu o podobne pomysły. Ale myślę, że na razie trzymam się mojej aktualnej wersji.

5

Inne rozwiązanie: zainstaluj pakiet bytestring-lexing i użyj readDouble, który zoptymalizowałem dla Ciebie.

cabal install bytestring-lexing 

Pakiet zapewnia optimized parsing functions dla pływających literały punkcie:

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