我正在编写我的第一个Clojure程序。
我正在使用clojure.data.csv来处理csv文件。我的文件可能很大,所以我想利用懒惰。我的MWE代码演示了我的问题如下所示。
当我执行加载数据函数时,我会得到"IOException流关闭“,所以在我看来,懒散流在消费点之前就已经关闭了。
我已经查看了data.csv (https://github.com/clojure/data.csv)的文档,并且可以看到,阻止流在消费之前关闭的一种方法是将打开的流移动到使用流的调用堆栈。据我所知,这是我在下面所做的,因为(采取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))))发布于 2018-02-10 17:24:45
正如您所说的,从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这里的重要观察是,最后的操作是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))))我的文件可能很大,所以我想利用懒惰。
您需要一种在流关闭之前完成所有工作的方法,这样您就可以向您的load-data函数提供一个函数,以便在CSV的每一行上执行:
(defn load-data [from f]
(with-open [reader (io/reader from)]
(doall (map f (csv/read-csv reader)))))例如,将行值串联成字符串:
(load-data (io/resource "input.txt")
(partial apply str))
=> ("abc" "efg")发布于 2018-04-10 22:17:30
如果您想要一个懒惰的解决方案,那么请查看https://stackoverflow.com/a/13312151/954570 (所有的学分都归原始作者https://stackoverflow.com/users/181772/andrew-cooke和https://stackoverflow.com/users/611752/johnj)。
这样做的目的是手动管理读取器的打开/关闭,并使读取器保持打开状态,直到序列耗尽。它有它自己的怪癖,但对我来说效果很好(我需要合并/处理来自multiple1大文件的数据,而这些数据不适合内存)。
https://stackoverflow.com/questions/48723378
复制相似问题