Pointers clojure中的指针循环

Pointers clojure中的指针循环,pointers,data-structures,clojure,immutability,Pointers,Data Structures,Clojure,Immutability,我正在编写一个解析XML的clojure程序。作为其中的一部分,我希望基于clojure.XML/parse函数在XML文档中创建一个节点树。但是,我希望树是双向的——也就是说,每个节点都有一个子节点列表和一个指向其父节点的指针。只有一个问题:所有数据都是不可变的,因此我无法在不更改子对象的情况下“添加”指向父对象的指针,从而使父对象的指针变得无用 我找到了这个答案: 解决方案建议创建一个单独的索引映射,该映射引用其中的对象。对于一个更糟糕的解决方案来说,这似乎是一个巨大的工作量。我不会有任何问

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

我找到了这个答案:

解决方案建议创建一个单独的索引映射,该映射引用其中的对象。对于一个更糟糕的解决方案来说,这似乎是一个巨大的工作量。我不会有任何问题的树是可变的,在建设过程中,但我不知道如何才能做到这一点。真的没有办法在clojure中获得循环指针吗


谢谢

逻辑上不可能使纯不可变结构循环,因为通过添加父指针或子指针,您将改变结构

有一种方法可以奏效,但我不确定我是否会推荐它:你可以将原子放入Clojure数据结构中,然后对这些结构进行变异,形成必要的链接。e、 g

(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样式的位置。

类似这样的内容可以:

(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的一部分,并且在构建之后永远不要触及可变字段,这样基本上就有了一个不可变的树。然而,在施工后修改上述树木将是困难的。YMMV.

您考虑过使用吗

拉链的设计允许与树状结构有效地工作。它包括一些基本操作,如查看和查看节点的位置,同时也允许通过结构

Zipper是相当通用的,包含的函数已经允许从XML创建它们。页面上的一个示例提供了如何使用它们的良好初始图片

对于XML拉链,您需要使用

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

创建初始结构。完成后,只需调用
root
即可获得结束结构。

谢谢-这似乎正是我想要的。尽管我对clojure如何处理这件事并不满意……clojure是一种相当“固执己见”的语言,强烈鼓励你走不变的道路。如果你想充分享受Clojure的优势,我认为,拥抱它总比与它斗争好。。。。。从长远来看,我相信这种方法将帮助我们制作出更好的软件。“从逻辑上讲,让纯粹的不可变结构循环是不可能的”——在Clojure,也许是这样。在哈斯克尔,这是可能的,至少在有限的范围内是可能的。但是剩下的答案是+1。嘿@mikera,在构建图形时,在返回图形之前,将所有内容都设置为(持久)时,对:子对象和:父对象使用(transient[])会有什么问题?除了不能打印这样的结构之外,这应该不是一个问题,对吗?在纯FP设置中处理XML的正确方法是使用拉链。