Existen varias formas de hacerlo, dependiendo de lo que desee exactamente.
Si usted tiene un function
que desea aplicar a cada línea en un archivo, se puede utilizar un código similar a la respuesta de Abhinav:
(with-open [rdr ...]
(doall (map function (line-seq rdr))))
Esto tiene la ventaja de que se abra el archivo, se procesa y cerrado lo más rápido posible, pero obliga a consumir todo el archivo de una vez.
Si desea retrasar el procesamiento del archivo que podría estar tentado a regresar las líneas, pero esto no funcionará:
(map function ; broken!!!
(with-open [rdr ...]
(line-seq rdr)))
porque el archivo se cierra cuando with-open
devoluciones, que es antes de, procesa el archivo de forma perezosa.
Una forma de evitar esto es para tirar todo el archivo en memoria con slurp
:
(map function (slurp filename))
que tiene una desventaja obvia - el uso de memoria - pero garantiza que no dejar el archivo abierto.
Una alternativa es dejar el archivo abierto hasta llegar al final de la lectura, al tiempo que genera una secuencia perezosa:
(ns ...
(:use clojure.test))
(defn stream-consumer [stream]
(println "read" (count stream) "lines"))
(defn broken-open [file]
(with-open [rdr (clojure.java.io/reader file)]
(line-seq rdr)))
(defn lazy-open [file]
(defn helper [rdr]
(lazy-seq
(if-let [line (.readLine rdr)]
(cons line (helper rdr))
(do (.close rdr) (println "closed") nil))))
(lazy-seq
(do (println "opening")
(helper (clojure.java.io/reader file)))))
(deftest test-open
(try
(stream-consumer (broken-open "/etc/passwd"))
(catch RuntimeException e
(println "caught " e)))
(let [stream (lazy-open "/etc/passwd")]
(println "have stream")
(stream-consumer stream)))
(run-tests)
que imprime:
caught #<RuntimeException java.lang.RuntimeException: java.io.IOException: Stream closed>
have stream
opening
closed
read 29 lines
mostrando que el wasn de archivos incluso se abrió hasta que fue necesario.
Este último enfoque tiene la ventaja de que puede procesar la secuencia de datos "en otro lugar" sin guardar todo en la memoria, pero también tiene una desventaja importante: el archivo no se cierra hasta que se lee el final de la secuencia. Si no tiene cuidado, puede abrir muchos archivos en paralelo, o incluso olvidarse de cerrarlos (al no leer la secuencia por completo).
La mejor elección depende de las circunstancias: se trata de una compensación entre la evaluación diferida y los recursos limitados del sistema.
PD: ¿Se ha definido lazy-open
en algún lugar de las bibliotecas? Llegué a esta pregunta tratando de encontrar esa función y terminé escribiendo la mía, como se indica arriba.
Muchas gracias, pero ¿y si quisiera mantener toda la lista en la memoria (no ser flojo), cuál sería la mejor manera entonces? Como dijiste para algunas operaciones, necesito repasar la lista una y otra vez (supongamos que tengo suficiente memoria para mantener toda la lista). – Ali
En ese caso, simplemente mantenga una referencia al encabezado de la lista diferida. Se cargará perezosamente la primera vez y luego se mantendrá cargado. Algo así como: '(nombres de definición (with-open [rdr (clojure.java.io/reader"/ruta/a/nombres/archivo ")] (línea-seq rdr)))' –
Bueno, no creo asi que. Como ha rodeado "line-seq" con "with-open", la transmisión subyacente se cerrará automáticamente cuando regrese. Entonces no queda nada detrás de tus "nombres" var. Entonces, básicamente, tendría que 1: '(def rdr (clojure.java.io/reader"/ruta/a/nombres/archivo "))' luego 2: '(nombres de definición (line-seq rdr))' luego 3 : '(. rdr close)'. Finalmente, ahora puedes jugar con tus "nombres" como: '(nombres de conteo)' –