我试图在一个由原子支持的clojurescript中构建concept of a Cursor。游标是一种类似于递归拉链的机制,用于编辑不可变的嵌套关联数据结构。
我是Clojure的新手,你能帮助我发现我的错误吗?
(defprotocol Cursor
(refine [this path])
(set [this value])
(value [this]))
(defn- build-cursor* [state-atom paths]
(reify Cursor
(set [this value] (swap! state-atom (assoc-in @state-atom paths value)))
(refine [this path] (build-cursor* state-atom (conj paths path)))
(value [this] (get-in @state-atom paths))))
(defn build-cursor [state-atom]
(build-cursor* state-atom []))
(comment
(def s (atom {:a 42}))
(def c (build-cursor s))
(assert (= (value c) {:a 42}))
(set c {:a 43}) ;; WARNING: Wrong number of args (2) passed to quiescent-json-editor.core/set at line 1 <cljs repl>
(assert (= (value c) {:a 43}))
(def ca (refine c :a)) ;; WARNING: Wrong number of args (2) passed to quiescent-json-editor.core/refine at line 1 <cljs repl>
(assert (= (value ca) 43))
(set ca 44)
(assert (= (value ca) 43))
)
答案 0 :(得分:1)
我也是Clojure的新手,但我抓了一把,发现了两个问题。
首先,set
方法与核心库函数冲突(即使它在Cursor
协议中)。为了调试,我添加了下划线前缀来避免这种情况。
其次,似乎在根游标上调用_set
会破坏该值。我发现assoc-in没有像您期望的那样处理空路[]
:
(assoc-in {} [] {:a 7})
; {nil {:a 7}}
...这就是cond
中_set
的原因。
这是我的测试代码:
(ns cursory)
(defprotocol Cursor
(_path [this])
(_root [this])
(_refine [this path])
(_set [this value])
(_value [this]))
(defn- build-cursor* [state-atom paths]
(reify Cursor
(_path [this] paths)
(_root [this] @state-atom)
(_set [this value] (cond
(empty? paths) (reset! state-atom value)
:else (assoc-in @state-atom paths value)))
(_refine [this path] (build-cursor* state-atom (conj paths path)))
(_value [this] (get-in @state-atom paths))))
(defn build-cursor [state-atom]
(build-cursor* state-atom []))
(comment
(def s (atom {:a 42, :b 84}))
(def c (build-cursor s))
(_set c {:a 44, :b 88})
(_root c)
(_path c)
(_value c)
(def ca (_refine c :a))
(_path ca)
(_value ca)
(_set ca 42)
(get-in {:a 1 :b 2} [])
(assoc-in {} [:a :b] 7)
(assoc-in {} [] {:a 7})
(empty? [])
)