#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
orvec
близко к месту, где это необходимо. Если вы поместите его в конец длинной цепочки обработки, не очевидно, почему он там, и это может привести к ошибкам, когда кто-то изменяет или удаляет «ненужный» шаг. Особенно, если последний шаг такой же тонкий, как изменениеmapv
map
, например.4. Я не согласен с Аланом здесь. Я бы придерживался того, что
doall
у вас есть: вокруг тела кода внутриwith-open
. Прямое обертываниеdoall
line-seq
кода заставит все это считываться в память, и еслиfn
переданный ввод уменьшит размер данных — как это(map count x)
происходит — вам понадобится леньline-seq
для очень больших файлов.5. Хотя причиной является лень, выполнение всего внутри
with-file
имеет другой риск — он ожидаетfn
возврата последовательности. Например. если взрывается(with-file ... count)
. Я бы предпочел ожидатьfn
, что не буду ленивым.