2012-03-02 27 views
5

Hace poco hice Waterloo CCC y creo que Haskell es el lenguaje perfecto para responder este tipo de preguntas. Aún lo estoy aprendiendo. Sin embargo, estoy luchando un poco con la entrada.Haskell: lea un archivo por línea

Aquí es lo que estoy usando:

import IO 
import System.Environment 
import System.FilePath 

… 

main = do 
    name <- getProgName 
    args <- getArgs 
    input <- readFile $ 
     if not (null args) 
      then head args 
      else dropExtension name ++ ".in" 
    let (k:code:_) = lines input 
    putStrLn $ decode (read k) code 

Como se puede ver, estoy leyendo de la ruta del archivo de línea de comandos o desde j1.in dada, por ejemplo, si este programa se llama j1.hs y compilado a j1.

Solo estoy interesado en las dos primeras líneas del archivo, por lo que he usado la coincidencia de patrones para obtener esas líneas y vincularlas a k y code, en este ejemplo. Y entonces leo k como un entero y lo paso y la cadena de código a mi función decode, que imprimo.

Me pregunto si readFile está cargando todo el archivo en la memoria, lo que sería malo. Pero luego comencé a pensar, tal vez porque Haskell es flojo, solo lee las dos primeras líneas porque eso es todo lo que se pide más adelante. ¿Estoy en lo cierto?

Además, si hay algo con ese ejemplo de código que podría ser mejor o más idiomático, házmelo saber.

Respuesta

8

El documentation for readFile dice:

La función readFile lee un archivo y devuelve el contenido del archivo como una cadena. El archivo se lee perezosamente, a pedido, como con getContents.

Así que sí, solo leerá necesariamente las dos primeras líneas del archivo (el almacenamiento en búfer significa que probablemente leerá más detrás de las escenas). Pero esto es una propiedad de readFile específicamente, no de todas las funciones de E/S de Haskell en general.

Lazy I/O es una mala idea para los programas de E/S pesadas (por ejemplo, servidores web) pero funciona muy bien para programas simples que no hacen demasiada E/S.

+0

En un tutorial 'hSetBuffering stdin LineBuffering' vi utilizado para stdin porque la entrada solo se ingresaría una línea a la vez; ¿habría un equivalente para la entrada de archivos? Si existe, ¿tendría sentido usarlo o se consideraría una optimización prematura? – mk12

+3

No estoy de acuerdo con su punto de que la E/S esencialmente perezosa solo es útil cuando es trivial. Un ejemplo de una característica muy útil de la E/S perezosa, en configuraciones pesadas de E/S, es que, de manera predeterminada, el manejo de datos está en su lugar, lo que significa que es muy eficiente para grandes volúmenes de datos. – amindfv

+0

@amindfv: Él no dice que la E/S perezosa es _inútil_ para los grandes programas, él dice que es _ malo_. Lo que quiere decir es que la IO lenta a menudo conduce a pérdidas de recursos (aquí el archivo nunca se cierra porque no se lee hasta el final) que son difíciles de corregir en programas grandes y complejos. Se deben preferir soluciones basadas en principios para la transmisión (como Iteratee o el Conduit más reciente) porque le dan un mejor control. – Jedai

6

Sí, readFile es flojo. Si quiere ser explícito al respecto, puede usar:

import Control.Monad (replicateM) 
import System.IO 

readLines n f = withFile f ReadMode $ replicateM n . hGetLine 

-- in main 
    (k:code:_) <- readLines 2 filename 

Esto asegurará que el archivo se cierre lo antes posible.

Pero la forma en que lo ha hecho está bien.

3

readFile lee el archivo perezosamente, por lo que no leerá todo el archivo en la memoria a menos que use todo el archivo. Por lo general, no leerá exactamente las dos primeras líneas, ya que se lee en bloques, pero solo leerá tantos bloques como sea necesario para encontrar la segunda línea nueva.

2

E/S en Haskell no suele ser flojo. Sin embargo, la función readFile específicamente es floja.

Otros han dicho lo mismo. Lo que no he visto que nadie señale aún es que el archivo que ha abierto no se cerrará hasta que el programa finalice o se ejecute el recolector de elementos no utilizados. Eso solo significa que el manejador del archivo del sistema operativo puede mantenerse abierto más de lo necesario. En tu programa eso probablemente no es gran cosa. Pero en un proyecto más complicado, podría ser.

Cuestiones relacionadas