2012-02-14 56 views
8

我在写一个解析XML的clojure程序。作为其中的一部分,我希望基于clojure.xml/parse函数在XML文档中创建一个节点树。不过,我希望树是双向的 - 也就是说,每个节点都有一个子列表和一个指向其父节点的指针。只有一个问题:所有数据都是不可变的,所以我不能在不更改子级的情况下“添加”指向父级的指针,从而使父级指针无用。clojure中的指针循环

我找到了这样的回答:How can one create cyclic (and immutable) data structures in Clojure without extra indirection?

的解决方案建议似乎要创建一个单独的索引图,这是指内部的对象。对于更糟糕的解决方案来说,这似乎是一项巨大的工作量。我没有问题,因为树在施工过程中是可变的,但我无法弄清楚它是如何完成的。真的没有办法在clojure中获得循环指针吗?

谢谢!

+2

在纯FP设置中处理XML的正确方法是使用拉链。 http://clojuredocs.org/clojure_core/clojure.zip/xml-zip – 2012-02-14 12:16:53

回答

5

从逻辑上讲,不可能使纯不可变的结构成为循环的,因为通过添加父指针或子指针就可以改变结构。

虽然我不确定我会推荐它,但有一种破解方式可行:您可以将原子放入Clojure数据结构中,然后改变这些以创建必要的链接。例如

(def parent {:id 1 :children (atom nil) :parent (atom nil)}) 

(def child {:id 2 :children (atom nil) :parent (atom nil)}) 

(swap! (:children parent) conj child) 
(reset! (:parent child) parent) 

;; test it works 
(:id @(:parent child)) 
=> 1 

这是各种方式讨厌:

  • 它会导致你的REPL,如果你尝试并打印其中的一个,因为REPL并不期望循环的数据结构栈溢出。
  • 这是可变的,所以你失去了一成不变的数据结构的所有维护和并发性的好处(这是关于Clojure的最好的事情之一!)
  • 你需要,如果你想利用一个节点的完整副本复制它(例如,构建一个新的XML文档),因为它不再是一个不可变的值。
  • 在导航结构时,取消引用所有原子可能会变得混乱。
  • 你会混淆习惯于习惯Clojure的人。

所以,如果你真的想要这样做,尽管我个人认为,如果你的文件表示不可变,那么长远来看你仍然会好得多。如果您想要浏览结构,您可以在文档中使用更类似XPath样式的位置。

+0

谢谢 - 这似乎是我想要的。虽然我对clojure是如何处理这个问题并不满意...... – Gilthans 2012-02-14 11:21:33

+2

Clojure是一种非常“自以为是”的语言,强烈建议您沿着不可变的路线走下去。如果你想享受Clojure的全部优势,我认为最好接受它,而不是去争取它,而且从长远来看,我相信这种方法将帮助我们做出更好的软件。 – mikera 2012-02-14 11:45:51

+1

“在循环中使纯不可变的结构在逻辑上是不可能的” - 也许在Clojure中。在Haskell中,这是可能的,至少在一定程度上是可能的。但答案的其余部分为+1。 – 2012-02-14 12:12:32

0

像这样的东西可以工作:

(defprotocol TreeItemConstruction 
    (add-child [this child])) 

(deftype MyTree 
    [parent ^:unsynchronized-mutable children] 
    TreeItemConstruction 
    (add-child [this child] 
    (locking this 
     (set! children (conj children child))))) 

(defn my-tree 
    [parent] 
    (->MyTree parent [])) 

(def root (my-tree nil)) 
(def children (repeatedly 5 #(my-tree root))) 
(doseq [child children] (add-child root child)) 

让公众API的协议没有,从来没有施工后碰可变领域,基本上你有一个不变的树。但是修改后的树会很难。因人而异。

2

您是否考虑过使用zippers

拉链设计用于有效地处理树状结构。它包括基本操作,例如查看节点的childrenparent,同时允许通过该结构的easily iterating

拉链是相当通用的,包含的函数已经允许从XML创建它们。 Other Libraries页面上的示例为如何使用它们提供了一个很好的初始画面。

对于XML拉链,你要使用

(clojure.zip/xml-zip (clojure.xml/parse file)) 

创建初始结构。完成后,只需拨打root即可获得最终结构。