Передать отложенную последовательность файловых строк в функцию

#clojure

#clojure

Вопрос:

Почему этот фрагмент кода не работает?

 (defn with-file [fname fn]
  (with-open [r (reader fname)]
    (fn (line-seq r))))

(with-file "data/input_d2" (fn [x] (map count x)))
 

Я открываю файл, и пока он открыт, я передаю отложенную последовательность строк в предоставленную функцию.

Ошибка, которую я получаю, такова:

 Error printing return value (IOException) at java.io.BufferedReader/ensureOpen (BufferedReader.java:122).
Stream closed
 

Ответ №1:

Я только что понял это. Поскольку строка seq является отложенной, все операции над ней возвращают отложенный seq. Поэтому мой вызов функции with-file возвращает отложенный seq, который REPL позже пытается оценить, но к тому времени файл уже закрыт.

Поэтому нам нужно принудительно вычислить то, что возвращается из переданной функции.

 (defn with-file [fname fn]
  (with-open [r (reader fname)]
    (doall (fn (line-seq r)))))

(with-file "data/input_d2" (fn [x] (map count x)))
 

Комментарии:

1. Кроме того, пожалуйста, уточните имена аргументов, такие как fn => f или the-fn . Кроме того, я бы обернул line-seq непосредственно с doall помощью или vec как: (f (vec (line-seq r))) .

2. Не будет ли обертывание его напрямую с помощью doallповреждать всю цель использования отложенной последовательности? Все это будет загружено в память

3. Это было бы, но для большинства целей это не имеет значения. Если действительно не требуется отложенный конвейер обработки, мне нравится держать doall or vec близко к месту, где это необходимо. Если вы поместите его в конец длинной цепочки обработки, не очевидно, почему он там, и это может привести к ошибкам, когда кто-то изменяет или удаляет «ненужный» шаг. Особенно, если последний шаг такой же тонкий, как изменение mapv map , например.

4. Я не согласен с Аланом здесь. Я бы придерживался того, что doall у вас есть: вокруг тела кода внутри with-open . Прямое обертывание doall line-seq кода заставит все это считываться в память, и если fn переданный ввод уменьшит размер данных — как это (map count x) происходит — вам понадобится лень line-seq для очень больших файлов.

5. Хотя причиной является лень, выполнение всего внутри with-file имеет другой риск — он ожидает fn возврата последовательности. Например. если взрывается (with-file ... count) . Я бы предпочел ожидать fn , что не буду ленивым.