2012-08-16 60 views
7

我试图用Clojure从一个大的(> 1GB)文件中抓取5行。我几乎在那里,但看到一些奇怪的事情,我想了解发生了什么。懒洋洋地从大文件中提取行

到目前为止,我已经得到了:现在

(defn multi-nth [values indices] 
    (map (partial nth values) indices)) 

(defn read-lines [file indices] 
    (with-open [rdr (clojure.java.io/reader file)] 
    (let [lines (line-seq rdr)] 
     (multi-nth lines indices)))) 

(read-lines "my-file" [0])作品没有问题。然而,在传递给[0 1]我下面的堆栈跟踪:

java.lang.RuntimeException: java.io.IOException: Stream closed 
     Util.java:165 clojure.lang.Util.runtimeException 
     LazySeq.java:51 clojure.lang.LazySeq.sval 
     LazySeq.java:60 clojure.lang.LazySeq.seq 
     Cons.java:39 clojure.lang.Cons.next 
      RT.java:769 clojure.lang.RT.nthFrom 
      RT.java:742 clojure.lang.RT.nth 
     core.clj:832 clojure.core/nth 
     AFn.java:163 clojure.lang.AFn.applyToHelper 
     AFn.java:151 clojure.lang.AFn.applyTo 
     core.clj:602 clojure.core/apply 
     core.clj:2341 clojure.core/partial[fn] 
     RestFn.java:408 clojure.lang.RestFn.invoke 
     core.clj:2430 clojure.core/map[fn] 

看来,流被关闭之前,我可以从文件中读取第二行。有趣的是,如果我从(nth lines 200)之类的文件中手动拉出一行,则multi-nth调用适用于所有值< = 200.

任何想法发生了什么?

回答

9

map(和line-seq)返回惰性序列,因此在调用with-open返回时关闭文件时,不会读取任何行。

基本上,你需要用前打开返回时,您可以使用的doall来实现整个返回值:

(defn multi-nth [values indices] 
    (map (partial nth values) indices)) 

(defn read-lines [file indices] 
    (with-open [rdr (clojure.java.io/reader file)] 
    (let [lines (line-seq rdr)] 
     (doall (multi-nth lines indices))))) 

或类似的东西。请记住,当你搜索指定的行时,你的multi-n会保留在seq的最前面,这意味着它会将所有的行保存到内存中的最后一行 - 并且使用像这样的第n个字符表示你为每个索引重复执行line-seq - 您需要解决该问题。

更新:

这样的事情会起作用。这是一个有点丑,比我喜欢,但它显示的原则,我想:请注意,这里的索引需要有一个设置

(defn multi-nth [values indices] 
(keep 
    (fn [[number line]] 
    (if (contains? indices number) 
     line)) 
    (map-indexed vector values))) 

(multi-nth '(a b c d e) #{2 3}) 
    => c d 
+0

好一点。我是否需要使用较低级别的Java随机访问方法调用才能正确使用它? – 2012-08-16 22:40:34

+0

我认为你可以使用map-indexed和filter来获得非常清晰的代码。我会在一分钟内更新... – 2012-08-16 22:42:27

+0

啊好吧,那太好了。如果它在语法上比你想要的更丑,你可以使用保持索引并可能压缩过滤器函数。现在看看它... – 2012-08-16 23:23:00

5

with-file当文件执行完成后关闭文件。所以一旦执行multi-nth这个文件就关闭了,这意味着你最终会得到一个指向一个关闭文件的惰性序列。

(read-lines "my-file" [0])的作品,因为只有懒惰序列的第一个元素被实现。

要解决此问题,您需要强制要实现的序列与doall

(defn multi-nth [values indices] 
    (doall (map (partial nth values) indices))) 

对于一个非常详细的解释看https://stackoverflow.com/a/10462159/151650

+0

啊。说得通。谢谢! – 2012-08-16 22:34:22