xml – 从大型Clojure树结构中的延迟序列中删除元素,避免头部保
问题描述
为了在Clojure中处理大型数据结构,惰性序列提供了一种很好的惯用方法.人们需要谨慎避免头脑 我很难处理像这样的大型树结构: R Root __________|____________________ A B C,D,E,... 1st Level Children _______|_______ _______|_______ X Y Y ... Y X Y X Y Y ... Y X Y 2nd Level Children >所有节点都是带有键的地图:内容. any:content的值是该节点的所有子节点的延迟seq. 在处理完树之后,我想得到一个新树,其中所有Y节点都被删除: R ______|__________________ A B C,... _____|___ _____|___ X X ... X X X ... X 示例代码和进一步说明 ;; Generating example data ;;;;;;;;;;;;;;;;;;;;;;;;;; (defn root [content] {:tag :root :content content}) (defn lazy-elements [n tag content] (lazy-seq (repeat n {:tag tag :content content}))) (defn level-1 [content] (lazy-elements 3 :A content)) (defn level-2 [n] (concat (lazy-elements 10 :X '(:leaf)) (lazy-elements n :Y '(:leaf)))) (defn remove-nodes [node] (remove #(= (:tag %) :Y) node)) ;; Illustrating usage ;;;;;;;;;;;;;;;;;;;;; ;; runs and runs and runs... and eventually returns correctly (defn valid-run [] (->> (root (level-1 (level-2 1e8))) :content first :content remove-nodes)) ;; Does not terminate properly,runs out of memory (defn invalid-run [] (->> (root (level-1 (level-2 1e8))) :content (map :content) ; source of head retention (map remove-nodes))) (Gist available on GitHub) 第二个示例将崩溃(取决于可用内存,可能需要调整二级项目的数量).映射过 我能够使用有效运行之类的数据,将其放入var保持可变状态,为所有相关节点执行此操作 题 如何以功能性的声明式方式实现这一目标?理想情况下,我希望避免使用可变状态以及存在 资源 以下文章和片段是有关该问题方面的有趣读物: > XML manipulation in Clojure 更多背景 最终我需要这个来处理大型XML文件.大意味着> 1GB并且将其解析为树将无法在可用的情况下工作 我也可以将XML作为事件流处理,而不是将XML解析为树,例如通过data.xml/source-seq.但是, 解决方法
问题是你的2级节点都有指向相同的内存中延迟序列的指针,然后你多次映射该序列.如果您只是在第一个和第二个节点上创建了有效运行的进程,那么您将遇到同样的问题 – 节点数量并不重要,因为您使用任意两个节点来清空堆.在实际应用程序中,您从数据库或文件或其他任何地方读取这些节点,它们将指向不同的对象,您可以依次对它们进行懒惰处理.
如果您生成更具代表性的示例数据(即相同数据但没有结构共享),则可以在处理每个节点时对其进行GC: (defn root' [content] (fn [] {:tag :root :content (content)})) (defn lazy-elements' [n tag content] (repeatedly n (fn [] {:tag tag :content (content)}))) (defn level-1' [content] (fn [] (lazy-elements' 3 :A content))) (defn level-2' [n] (fn [] (concat (lazy-elements' 10 :X (fn [] '(:leaf))) (lazy-elements' n :Y (fn [] '(:leaf)))))) (defn remove-nodes [node] (remove #(= (:tag %) :Y) node)) (defn run [] (let [root-builder (root' (level-1' (level-2' 1e8)))] (->> (root-builder) :content (map :content) (map remove-nodes)))) user> (pprint (run)) (({:tag :X,:content (:leaf)} {:tag :X,:content (:leaf)}) ({:tag :X,:content (:leaf)})) 由于我们只是生成示例内容,因此我调整了所有节点构建器,而不是它们应该存储N个副本的对象,它们应该调用N次以获得N个不同对象的函数.而不是返回一个节点,它们返回一个函数,当被调用时,它生成该节点的副本;这使得它们可以像原始版本一样组合,只需要在外层进行一次额外的函数调用.如果你实际上已经拥有了不同的对象,我怀疑你会在一个真实的应用程序中,你可以只使用你写的原始函数. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |