2010-12-20 9 views
7

Estoy buscando una manera eficiente de leer números de un archivo de texto sin instalar paquetes adicionales. Data.ByteString.Lazy.Char8.readInt parece ser el truco para los enteros. He leído que ByteString tiene ahora un método readDouble, pero cuando escribo import Data.ByteString.Lex.Lazy.Double (readDouble) el compilador se queja:Lectura eficiente de números en 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 

Mi versión del paquete cadena de bytes es 0.9.1.5.

Entonces, ¿estoy haciendo algo mal? O tal vez hay una mejor solución para el problema? Gracias.

Actualización: OK, parece que readDouble está en el paquete bytestring-lexer que no está instalado por defecto. ¿Alguna otra idea?

+1

sólo tiene que instalar el paquete de cadena de bytes-lexer a continuación. "cabal install bytestring-lexer" – sclv

+1

Quiero prescindir de paquetes adicionales, porque mis programas se ejecutarán en servidores sobre los que no tengo control. – adamax

+0

@adamax: Vale la pena agregar esa restricción a su pregunta. –

Respuesta

3

La única vez que me encontré con el análisis de dobles en la ruta crítica, he usado esta:

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

No había nada que parecía un readDouble en cadena de bytes en ese momento, sin embargo. Esa sería probablemente una mejor solución si ahora es estándar.

+0

¡Gracias! He hecho algunos experimentos.Para facilitar las cosas, tomé atoi en lugar de atof y lo comparé con la función show habitual y mi implementación ingenua (iread). FFI supera por completo el show, sin embargo, pierde un 20% para iread. Tal vez, hay una sobrecarga causada por las conversiones a CString – adamax

2

Esto es lo que se me ocurrió.

Utilicé la función ofrecida por JB y agregué dos trucos que aprendí del código fuente de bytesbing-lexing (gracias, sclv!). La primera es esta función:

strict = SB.concat . LB.toChunks

Transforma una cadena de bytes perezoso en uno no perezoso de manera eficiente.

El segundo truco es la función Data.ByteString.Internal.inlinePerformIO, que es una variante más eficiente de la función no seguraPerformIO.

Aquí está el código completo que permite una lectura número bastante rápido:


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

Y un ejemplo de programa que calcula la suma de todos los números en la entrada:

 
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 se discute. Presumiblemente uno puede construir un readDouble basado en ideas similares. Pero creo que me quedaré con mi versión actual por ahora.

5

Otra solución: instale el paquete bytestring-lexing, y use readDouble, que optimicé para usted.

cabal install bytestring-lexing 

El paquete proporciona optimized parsing functions para los literales de punto flotante:

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