2014-01-24 62 views
1

我想了解一個惰性序列的行爲,如果我迭代doseq但保留第一個元素的一部分。line-seq的行爲是什麼?

(with-open [log-file-reader (clojure.java.io/reader (clojure.java.io/file input-file-path))] 

    ; Parse line parse-line returns some kind of representation of the line. 
    (let [parsed-lines (map parse-line (line-seq log-file-reader)) 
      first-item (first parsed-lines)] 

      ; Iterate over the parsed lines 
      (doseq [line parsed-lines] 
      ; Do something with a side-effect 
     ))) 

我不想保留任何列表,我只是想對每個元素執行一個副作用。我相信沒有first-item就沒有問題了。

我在我的程序中遇到了內存問題,我認爲在parsed-line序列的開始部分可能保留對某些內容的引用意味着存儲了整個序列。

這是什麼定義的行爲?如果正在存儲序列,是否有一種通用的方法來獲取對象的副本並使序列的已實現部分被垃圾收集?

回答

2

序列保持發生在這裏

... 
(let [parsed-lines (map parse-line (line-seq log-file-reader)) 
... 

線的文件中的序列正在懶惰地產生和分析,但在整個序列扶住,的let範圍內。該序列在doseq中實現,但doseq不是問題,它不執行序列控制。

... 
(doseq [line parsed-lines] 
; Do something 
... 

你未必在乎序列保持在let因爲let的範圍是有限的,但在這裏,想必你的文件大和/或你留的let的動態範圍內,一會兒,或者也許在「做某事」部分返回一個包含它的閉包。

請注意,保持序列中的任何給定元素(包括第一個元素)都不包含該序列。如果您認爲頭是Prolog中「頭銜列表」中的第一個元素,那麼頭銜控制就有點不恰當。問題在於引用序列。

+0

僅供參考我正在處理多千兆字節的文件。你是說,即使是懶惰地生產和消費,let也保留了序列?我應該使用什麼語法來完成上述操作?我剛剛重新編寫了一個循環+在seq-seq尾部重複使用,並且RAM使用量明顯更小。但它看起來不那麼好。有沒有一種方法可以在沒有序列控制的情況下得到一張懶惰評估過的地圖? – Joe

+0

不需要回答,如果你不想,我意識到綁定是在cons-cell-type-element而不是「整個懶惰序列」上。我應該這樣做的方式是將'parsed-lines'放入'doseq'綁定中。 – Joe

+1

編譯器應在最後一次引用時釋放'parsed-lines',即doseq開始時。這裏發佈的代碼不需要大量的內存,除非註釋掉的行也指向大型的懶惰序列。 – amalloy

1

JVM一旦成爲Java堆的一部分就不會將內存返回到操作系統,除非以不同的方式配置它,否則默認最大堆大小相當大(通常爲可用RAM的1/4)。所以如果你只遇到像「天哪,這佔用了大量內存」這樣的模糊問題,而不是「JVM拋出OutOfMemoryError」,你可能只是沒有按照你喜歡的方式調整JVM法案。 partition-by是一個急於,因爲它一次保存在內存中的一個或兩個分區,但除非你的分區是巨大的,你不應該用這段代碼用完堆空間。嘗試設置-Xmx100m,或者您認爲適合您程序的合理堆大小,然後查看是否有問題。

+0

感謝您的幫助。是的,分區非常大(〜OTOO 1 GB)。我已經重複了幾次代碼,我將不得不返回並確認特性並在星期一更新。這個操作完成後,JVM返回大量內存,這就是爲什麼我很可疑。我的堆設置足夠大(4GB),但我只是看到使用量隨着數據量的增加而增加,並推斷大型真實世界數據的消耗量,我認爲存在一些問題。 – Joe

+0

re'partition-by'我聽說它返回了lazy seqs的lazy seqs。你說的是分區本身是被實現和存儲的。如果分區存儲整個那麼這將回答我的問題(他們非常大)。 – Joe

+0

真正的渴望seqs懶惰seqs。 – amalloy