2012-04-03 33 views
3

我写这个函数,但它的返回值是什么。这个函数的返回值是什么?

(defn read-data [file] 
    (let [code (subs (.getName file) 0 3)] 
    (with-open [rdr (clojure.java.io/reader file)] 
     (drop 1 (line-seq rdr))))) 

(def d (read-data "data.db")) 

直到现在。但是,当我想要打印出来。

(clojure.pprint/pprint d) 

我有一个例外:

Exception in thread "main" java.lang.RuntimeException: java.io.IOException: Stream closed 

让我糊涂了,有什么不对?返回值不是一个列表?如何在这种情况下作为新手进行调试?

谢谢!

回答

3

问题是line-seq是懒惰的,读者在评估时关闭。

这意味着需要在with-open范围内读取所有行。一种选择是使用doall强制对line-seq进行全面评估,如下所示。

(drop 1 (doall (line-seq rdr))) 

这种方法的一个潜在问题是,如果文件大于可用内存越大,你会得到一个OutOfMemoryError。所以,根据你想要完成的工作,可能会有其他更少的内存密集型解决方案。

+0

你是第一个。谢谢! – Kane 2012-04-03 04:22:31

1
(drop 1 (line-seq rdr)) 

该行返回一个序列,即延迟或流。所以基本上,在执行该行时实际上并不读取文件读取器rdr,但是当您试图在打印方法中访问此序列时,rdr正在被line-seq函数读取,但是如您使用with-open,该rdr已经当执行流出with-open时关闭。

请参考doall来创建一个惰性序列,并在创建时进行评估。我建议你阅读一般关于FP的懒惰以及特别是Clojure的序列,以便更好地理解懒惰评估。

2

作为例子,你可以使用关闭和修改 “行-seq的” 自动关闭读者:

(defn line-seq2 [^java.io.BufferedReader rdr] 
    (if-let [line (.readLine rdr)] 
    (cons line (lazy-seq (line-seq2 rdr))) 
    (.close rdr))) 

(defn my-reader [file] 
    (let [lines (line-seq (clojure.java.io/reader file))] 
    (fn [] lines))) 

(def d (my-reader "data.db")) 

现在变量 “d” 是一个功能:

user> (drop 1 (d)) 
=> ... file body except first line ...