Lectura de archivos Haskell


Recientemente he comenzado a aprender Haskell y estoy teniendo muchos problemas tratando de averiguar cómo funciona la lectura de archivos.

Por ejemplo, tengo un archivo de texto "test.txt" Y contiene líneas de números por ejemplo:

32 4
2 30
300 5

Quiero leer cada línea y luego evaluar cada palabra y agregarlas. Así que estoy tratando de hacer algo como esto hasta ahora:

import System.IO  
import Control.Monad

main = do  
        let list = []
        handle <- openFile "test.txt" ReadMode
        contents <- hGetContents handle
        singlewords <- (words contents)
        list <- f singlewords
        print list
        hClose handle   

f :: [String] -> [Int]
f = map read

Sé que esto es completamente incorrecto, pero no se como usar la sintaxis correctamente. Cualquier ayuda será en gran medida apreciar. Así como enlaces a buenos tutoriales que tienen ejemplos y explicación de código excepto este: http://learnyouahaskell.com/input-and-output lo he leído completamente

Author: DustBunny, 2011-10-23

3 answers

No es un mal comienzo! Lo único que hay que recordar es que la aplicación pure function debe usar let en lugar del enlace <-.

import System.IO  
import Control.Monad

main = do  
        let list = []
        handle <- openFile "test.txt" ReadMode
        contents <- hGetContents handle
        let singlewords = words contents
            list = f singlewords
        print list
        hClose handle   

f :: [String] -> [Int]
f = map read

Este es el cambio mínimo necesario para que la cosa se compile y ejecute. Estilísticamente, tengo algunos comentarios:

  1. La unión list dos veces parece un poco sombría. Tenga en cuenta que esto no está mutando el valor list 's sino que está sombreando la definición antigua.
  2. Funciones puras en línea mucho más!
  3. Cuando sea posible, usar readFile es es preferible abrir, leer y cerrar un archivo manualmente.

La implementación de estos cambios da algo como esto:

main = do  
        contents <- readFile "test.txt"
        print . map readInt . words $ contents
-- alternately, main = print . map readInt . words =<< readFile "test.txt"

readInt :: String -> Int
readInt = read
 46
Author: Daniel Wagner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-23 17:21:40

La solución de Daniel Wagner es excelente. Aquí hay otro giro para que pueda obtener más ideas sobre el manejo eficiente de archivos.

{-#  LANGUAGE OverloadedStrings #-}
import System.IO
import qualified Data.ByteString.Lazy.Char8 as B
import Control.Applicative
import Data.List

sumNums :: B.ByteString -> Int
sumNums s = foldl' sumStrs 0 $ B.split ' ' s

sumStrs :: Int -> B.ByteString -> Int
sumStrs m i = m+int
              where Just(int,_) = B.readInt i

main = do 
  sums <- map sumNums <$> B.lines <$> B.readFile "testy"
  print sums

Primero, verás el pragma OverloadedStrings. Esto permite usar comillas normales para literales de cadena que son en realidad bytestrings. Usaremos Lazy ByteStrings para procesar el archivo por varias razones. Primero, nos permite transmitir el archivo a través del programa en lugar de forzarlo todo a la memoria a la vez. También, las bytestrings son más rápidas y eficientes que las cadenas en general.

Todo lo demás es bastante sencillo. Leemos el archivo en una lista perezosa de líneas, y luego asignamos una función de suma sobre cada una de las líneas. Los <$> son solo atajos que nos permiten operar en el valor dentro del funtor IO() if si esto es demasiado, me disculpo. Solo quiero decir que cuando usted readFile usted no consigue detrás un ByteString, usted consigue detrás un ByteString envuelto en IO un IO (ByteString). El <$> dice "Hey' Quiero operar la cosa dentro del IO y luego envolverlo de nuevo.

B. split separa cada línea en números basados en espacios en blanco. (También podríamos usar B. words para esto) La única otra parte interesante es la in sumStrs usamos deconstrucción/coincidencia de patrones para extraer el primer valor del Solo que es devuelto por la función readInt.

Espero que esto haya sido útil. Pregunte si tiene alguna pregunta.

 6
Author: Erik Hinton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-23 17:32:12

Para todos los programadores no funcionales que hay aquí es una delicia

unsafePerformIO . readFile $ "file.txt"

Lee un archivo en una cadena

No hay cadena IO, solo una cadena normal completamente cargada lista para usar. Esto puede no ser la manera correcta, pero funciona y no es necesario cambiar sus funciones existentes para adaptarse a IO String

P. S. No te olvides de importar

import System.IO.Unsafe 
 0
Author: Kapytanhook,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-23 21:01:50