我正在读一篇关于懒惰序列如何导致OutOfMemoryError的文章,比如说,在大序列上使用循环/递归。我试图从内存中加载一个3MB的文件来处理它,我想这正发生在我身上。但是,我不知道是否有一种惯用的方法来修复它。我试着放入doall's,但我的程序似乎没有终止。小输入可以工作:
小输入(文件内容):AAABBBCCC正确输出:((65 65) (65 66) (66 66) (67 67) (67 67))
代码:
(def file-path "/Users/me/Desktop/temp/bob.txt")
;(def file-path "/Users/me/Downloads/3MB_song.m4a")
(def group-by-twos
(fn [a-list]
(let [first-two (fn [a-list] (list (take 2 a-list)))
the-rest-after-two (fn [a-list] (rest (rest a-list)))
only-two-left? (fn [a-list] (if (= (count a-list) 2) true false))]
(loop [result '() rest-of-list a-list]
(if (nil? rest-of-list)
result
(if (only-two-left? rest-of-list)
(concat result (list rest-of-list))
(recur (concat result (first-two rest-of-list))
(the-rest-after-two rest-of-list))))))))
(def get-the-file
(fn [file-name-and-path]
(let [the-file-pointer
(new java.io.RandomAccessFile (new java.io.File file-name-and-path) "r")
intermediate-array (byte-array (.length the-file-pointer))] ;reserve space for final length
(.readFully the-file-pointer intermediate-array)
(group-by-twos (seq intermediate-array)))))
(get-the-file file-path)正如我上面所说的,当我在一堆地方放置doall时,它似乎没有结束。我怎样才能让它在大文件上运行,有没有办法摆脱做我需要做的任何事情的认知负担?一些规则?
发布于 2011-11-12 16:03:23
你完全是在内存中读取文件,然后在这个字节数组上创建一个seq,这并没有给你带来任何延迟序列的好处,因为所有需要的数据都已经加载到内存中了,而延迟序列实际上意味着在需要的时候产生/生成数据。
您可以使用如下命令在文件内容上创建seq:
(def get-the-file
(fn [file-name-and-path]
(let [the-file-pointer
(new java.io.RandomAccessFile (new java.io.File file-name-and-path) "r")
file-len (.length the-file-pointer)] ;get file len
(partition 2 (map (fn [_] (.readByte the-file-pointer)) (range file-len))))))注意:我还没有真正尝试过它,但我希望它至少能让您对延迟文件读取部分有所了解
发布于 2011-11-12 16:37:18
我想一个惯用的解决方案是:
(partition 2 (map int (slurp "/Users/me/Desktop/temp/bob.txt")))这并不是完全懒惰,因为完整的文件被加载到内存中,但对于不太大的文件,它应该可以正常工作。然而,分区和映射是懒惰的,所以如果你用一个缓冲的读取器代替slurp,你将得到一个完全懒惰的版本。
注意:如果文件的大小是奇数,这将吞噬最后一个字符。如果大小是奇数,还不清楚你所期望的是什么。如果您想让最后一个值出现在它自己的列表中,可以使用(partition 2 2 [] ... )
user=> (partition 2 (map int "ABCDE"))
((65 66) (67 68))
user=> (partition 2 2 [] (map int "ABCDE"))
((65 66) (67 68) (69))发布于 2011-11-14 01:29:47
在处理大量数据时,请注意clojure数据结构。(典型的Clojure应用程序使用的内存是相同Java应用程序的两到三倍-序列是内存昂贵的)。如果您可以将整个数据读取到一个数组中,就可以这样做。然后处理它,同时确保不保留对任何序列头部的引用,以确保垃圾收集在过程中发生。
此外,字符串比char基元大得多。单个字符字符串为26个字节,字符为2个字节。即使你不喜欢使用数组,arraylist也比序列或向量小好几倍。
https://stackoverflow.com/questions/8102972
复制相似问题