我正在编写一个Clojure库来解析Mac的基于XML的属性列表文件。除非您给它一个大的输入文件,否则代码工作得很好,这时您将得到java.lang.OutOfMemoryError: Java heap space。
下面是一个输入文件示例(足够小到可以正常工作):
<plist version="1.0">
<dict>
<key>Integer example</key>
<integer>5</integer>
<key>Array example</key>
<array>
<integer>2</integer>
<real>3.14159</real>
</array>
<key>Dictionary example</key>
<dict>
<key>Number</key>
<integer>8675309</integer>
</dict>
</dict>
</plist>clojure.xml/parse将其转化为:
{:tag :plist, :attrs {:version "1.0"}, :content [
{:tag :dict, :attrs nil, :content [
{:tag :key, :attrs nil, :content ["Integer example"]}
{:tag :integer, :attrs nil, :content ["5"]}
{:tag :key, :attrs nil, :content ["Array example"]}
{:tag :array, :attrs nil, :content [
{:tag :integer, :attrs nil, :content ["2"]}
{:tag :real, :attrs nil, :content ["3.14159"]}
]}
{:tag :key, :attrs nil, :content ["Dictionary example"]}
{:tag :dict, :attrs nil, :content [
{:tag :key, :attrs nil, :content ["Number"]}
{:tag :integer, :attrs nil, :content ["8675309"]}
]}
]}
]}我的代码将其转换为Clojure数据结构。
{"Dictionary example" {"Number" 8675309},
"Array example" [2 3.14159],
"Integer example" 5}我代码的相关部分如下
; extract the content contained within e.g. <integer>...</integer>
(defn- first-content
[c]
(first (c :content)))
; return a parsed version of the given tag
(defmulti content (fn [c] (c :tag)))
(defmethod content :array
[c]
(apply vector (for [item (c :content)] (content item))))
(defmethod content :dict
[c]
(apply hash-map (for [item (c :content)] (content item))))
(defmethod content :integer
[c]
(Long. (first-content c)))
(defmethod content :key
[c]
(first-content c))
(defmethod content :real
[c]
(Double. (first-content c)))
; take a java.io.File (or similar) and return the parsed version
(defn parse-plist
[source]
(content (first-content (clojure.xml/parse source))))代码的核心是content函数,这是一个在:标记( XML的名称)上分派的multimethod方法。我想知道,为了使这个递归更好地工作,我是否应该做一些不同的事情。我试着用trampoline content替换对trampoline content的所有三个调用,但都没有效果。为了让这种相互递归更有效地工作,我应该做些什么呢?还是我采取了根本错误的做法?
编辑:顺便说一下,这段代码是可在GitHub上获得,它的形式可能更容易使用。
发布于 2011-02-02 06:55:56
您有来自单个方法的多个递归调用(每个子方法一个),因此您的代码不是(也不可能没有繁重的reorg)尾递归。trampoline用于互尾递归函数.
您的大型XML文件有多深,有多长?我这么问是因为你得到的是OoM,不是这样的。
无论如何,要解决递归问题(不太可能导致异常),您必须遍历XML (例如使用xml-zip),同时维护一个表示正在构建的结果树的堆栈(向量或列表)。具有讽刺意味的是,XML数据结构的遍历在某种程度上等同于用于构建该结构的sax事件。
发布于 2011-02-02 06:39:32
大量递归将导致StackOverflowException,而不是OutOfMemoryError。此外,这里的递归似乎不太深入(根据示例中的XML文件,只有3个级别)。
我的猜测是,抛出OutOfMemoryError是因为您的大型XML文件被解析到的数据结构太大,无法适应JVM堆。您可以使用-Xms和-Xmx选项尝试增加堆大小。但是,解析大型XML文件的正确方法是使用SAX事件,而不是构建树(DOM或Clojure数据结构)。
https://stackoverflow.com/questions/4867013
复制相似问题