如何在没有流被关闭之前正确地打开处理?

Vis*_*a V 5 clojure

我正在写我的第一个Clojure计划.

我正在使用clojure.data.csv来处理csv文件.我的文件可能很大,所以我确实想利用懒惰.我的MWE代码演示了我的问题如下所示.

当我执行load-data函数时,我得到"IOException Stream closed",所以我很清楚懒惰流在消费点之前被关闭了.

我查看了data.csv(https://github.com/clojure/data.csv)的文档,可以看到一种方法可以阻止流在关闭之前被关闭以将流开放移动到callstack流被消耗.据我了解,这是我在下面所做的,因为(采取5)是在开放的范围内.显然,我有一个概念上的差距.非常感谢任何帮助!

(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))))
Run Code Online (Sandbox Code Playgroud)

Tay*_*ood 7

正如你所说,你所返回的load-data是一个懒惰的序列,当它被消耗时,你已经离开了范围with-open.你只需要在返回之前强制实现延迟序列.

据我所知,这是我在下面所做的事情,因为它(take 5)是在我的范围内with-open.

它在范围内,但take也返回一个懒惰的序列!它只在另一个中包含了一个懒惰序列,直到with-open范围之后才会实现.从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
Run Code Online (Sandbox Code Playgroud)

这里重要的观察是最终操作reduce将消耗懒惰序列.如果您reduce取出并试图从函数外部使用生成的序列,则会得到相同的"流闭合"异常.

实现此目的的一种方法是将序列转换为带有vec或使用的向量,doall这也将迫使它被实现:

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

我的文件可能很大,所以我确实想利用懒惰.

在流关闭之前,您需要一种方法来完成所有工作,因此您可以为函数提供load-data函数以在CSV的每一行上执行:

(defn load-data [from f]
  (with-open [reader (io/reader from)]
    (doall (map f (csv/read-csv reader)))))
Run Code Online (Sandbox Code Playgroud)

例如,将行值连接成字符串:

(load-data (io/resource "input.txt")
           (partial apply str))
=> ("abc" "efg")
Run Code Online (Sandbox Code Playgroud)