我正在写我的第一个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)
正如你所说,你所返回的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)