2012-02-14 54 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即可獲得最終結構。