Я пишу библиотеку Clojure для анализа XML-файлов списка свойств Mac OS X . Код работает нормально, если вы не предоставите ему большой входной файл, после чего вы получите 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). Мне интересно, есть ли что-то другое, что я должен делать, чтобы эта рекурсия работала лучше. Я попытался заменить все три звонка на content
на trampoline content
, но это не сработало. Есть ли что-нибудь необычное, что я должен сделать, чтобы эта взаимная рекурсия работала более эффективно? Или я придерживаюсь принципиально неправильного подхода?
Редактировать: Кстати, этот код доступен на GitHub , в таком виде с ним легче играть.