如何表示两个records/maps之间的父子关系?

How to represent parent-child relationship between two records/maps?

我试图在 Clojure/Script 中表示 gui 元素。问题是元素是嵌套的,需要相互通信。我想互相传递引用,但似乎我不能像我天真地想的那样只是互相传递引用。

我试过的代码

(defrecord Element [position size parent children state type value])
(def parent (atom (Element. ...)))
(def child  (atom (Element. ...)))
(assoc @parent :child child)
(assoc @child :parent parent)

这行不通。

对于此类问题,Clojure 中惯用的解决方案是什么?


更新:相互引用似乎不起作用:

(defrecord Element [position size parent children state type value])

(def parent (atom (Element. {:x 0 :y 0} {:width 1600 :height 900} nil nil :collapsed :container nil)))

(def child (atom (Element. {:x 5 :y 5} {:width 100 :height 100} nil nil :collapsed :container nil)))

(swap! parent assoc :child child)
(swap! child assoc :parent parent)

#object[InternalError InternalError: too much recursion]
   cljs.core.PersistentArrayMap.prototype.cljs$core$ILookup$_lookup$arity (jar
:file:/C:/Users/user/.m2/repository/org/clojure/clojurescript/1.10.520/clojuresc
ript-1.10.520.jar!/cljs/core.cljs:6811:10)

也许在 Clojure 中你不做相互引用?

assoc 将 parent 转换为 child,但随后丢弃了新的 Element。您需要使用 reset!swap!:

设置原子中的值
(swap! parent assoc :child child)  ; Equivalent to (swap! parent #(assoc % :child child))
(swap! child assoc :parent parent)

swap! is similar to update,但它替换了 atom.

中的值

另请注意,您为字段指定了名称 children,但在您的示例中,您使用 :child 引用它。 children 建议使用多个 children,所以我将其更改为以向量开始而不是 nil,然后是 conj。我还会创建一个 new-element 助手:

(defrecord Element [position size parent children state type value])

(defn new-element [position size]
  (->Element position size nil [] :collapsed :container nil))

(def parent (atom (new-element {:x 0 :y 0} {:width 1600 :height 900})))
(def child (atom (new-element {:x 5 :y 5} {:width 100 :height 100})))

(swap! parent update :children conj child)
(swap! child assoc :parent parent)

为了解决您遇到的错误,它在 Clojure.

中对我来说运行良好

如果我在上面的代码运行后打印 parentchild,我会得到一个无限的输出,因为要打印 parent 你需要打印 child,要打印 child,您需要打印 parent... 不过它对我仍然有效。

确保不会出现错误,因为您正在尝试在 REPL 中打印结构。

这也可能是 Clojurescript 的限制。很遗憾,我无法回答这个问题,因为我不熟悉 Cljs。

如果这最终没有帮助,我会删除我的答案。


我还会注意到嵌套原子不是一个好主意。 atoms 是真正可变的容器来容纳不可变的 objects。拥有一个 ID 系统并让每个 parent/child 持有一个数字 ID 到它的 child/parent 可能会更干净。然后你的整个程序只有一个原子。原子可以保存 ID-> 元素的映射,以便于查找。