5 votes

Comment gérer correctement la fonction with-open sans que le flux ne soit fermé avant d'être consommé ?

J'écris mon premier programme en Clojure.

J'utilise clojure.data.csv pour traiter un fichier csv. Mon fichier est potentiellement grand et je veux donc exploiter la paresse. Mon code MWE pour démontrer mon problème est montré ci-dessous.

Lorsque j'exécute la fonction load-data, j'obtiens "IOException Stream closed" et il est donc clair pour moi que le flux paresseux est fermé avant le point de consommation.

J'ai consulté la documentation relative au fichier data.csv ( https://github.com/clojure/data.csv ) et on peut voir qu'une façon d'empêcher la fermeture du flux avant sa consommation est de déplacer l'ouverture du flux dans la pile d'appels où le flux est consommé. D'après ce que je comprends, c'est ce que j'ai fait ci-dessous puisque (take 5) est dans les limites de with-open. Il est clair que j'ai une lacune conceptuelle. J'apprécie énormément toute aide !

(ns data-load.core
  (:gen-class)
  (:require [clojure.data.csv :as csv]
            [clojure.java.io :as io]))

(defn load-data [from to]
   (with-open [reader (io/reader from)
               writer (io/writer to)]
              (->> (csv/read-csv reader)
              (take 5))))

7voto

Taylor Wood Points 12375

Comme vous l'avez dit, ce que vous revenez de load-data est une séquence paresseuse, qui, au moment où elle est consommée, a déjà quitté le champ d'application de with-open . Il suffit de forcer la réalisation de la séquence paresseuse avant de la renvoyer.

Si j'ai bien compris, c'est ce que j'ai fait ci-dessous depuis (take 5) se trouve dans les limites de la with-open .

Elle entre dans le champ d'application, mais take renvoie également une séquence paresseuse ! Il a seulement enveloppé une séquence paresseuse dans une autre qui ne sera réalisée qu'après que with-open champ d'application. Tiré des exemples clojure.data.csv :

(defn sum-second-column [filename]
  (with-open [reader (io/reader filename)]
    (->> (read-column reader 1)
         (drop 1)
         (map #(Double/parseDouble %))
         (reduce + 0)))) ;; this is the only non-lazy operation

L'observation importante ici est que l'opération finale est reduce qui va consommer la séquence paresseuse. Si vous avez pris reduce et que vous essayez de consommer la séquence produite depuis l'extérieur de la fonction, vous obtiendrez la même exception "stream closed".

Une façon de procéder consiste à transformer la séquence en un vecteur avec l'option vec ou utiliser doall qui l'obligera également à se réaliser :

(defn load-data [from]
  (with-open [reader (io/reader from)]
   (->> (csv/read-csv reader)
        (take 5)
        ;; other intermediate steps go here
        (doall))))

Mon fichier est potentiellement volumineux et je souhaite donc exploiter la paresse.

Vous aurez besoin d'un moyen de faire tout votre travail avant que le flux ne soit fermé, donc vous pourriez fournir une fonction à votre load-data à exécuter sur chaque ligne du fichier CSV :

(defn load-data [from f]
  (with-open [reader (io/reader from)]
    (doall (map f (csv/read-csv reader)))))

Par exemple, concaténer les valeurs des lignes en chaînes de caractères :

(load-data (io/resource "input.txt")
           (partial apply str))
=> ("abc" "efg")

0voto

vitaly Points 582

Si vous souhaitez une solution plus simple, consultez le site suivant https://stackoverflow.com/a/13312151/954570 (tous les crédits vont aux auteurs originaux https://stackoverflow.com/users/181772/andrew-cooke y https://stackoverflow.com/users/611752/johnj ).

L'idée est de gérer manuellement l'ouverture/la fermeture du lecteur et de maintenir le lecteur ouvert jusqu'à ce que la séquence soit épuisée. Cette méthode comporte ses propres particularités, mais elle a bien fonctionné pour moi (j'avais besoin de fusionner/traiter des données provenant de plusieurs1 grands fichiers qui ne tiennent pas dans la mémoire).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X