2012-04-03 31 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 ...