Clojure Zipper over a heterogeneous tree
-
02-07-2021 - |
Question
I'm having problems writing a zipper that can traverse a heterogeneous tree of nodes. I have i) a list of maps. Each map has ii) an :inputs key, whose value is a list of maps.
I want to use a zipper to visit each of those leaves and add a computed value. The code block and input tree data are below. I can't seem to get the zipper code to visit the leaf nodes, and add { :thing 123 }. I must be missing something simple. Any insights?
A)
(loop [loc (zip/zipper (or map? list?) #((if (map? %1) ;; get children of a node (:inputs %1) %1 )) #(%1) ;; create a new node (:input-layer neural-network)) ] (if (zip/end? loc) (zip/root loc) (if (map? loc) (recur (zip/next (zip/edit loc merge { :thing 123 } ))) (recur (zip/next loc)) ) ) )
B)
({:inputs ({:key :avolume, :value 2.25, :weight 0.4283380545172636, :bias 0} {:key :bvolume, :value 3.0, :weight 0.6970037374879661, :bias 0} {:key :ask, :value 1.32379, :weight 0.5387437158323669, :bias 0} {:key :bid, :value 1.3239, :weight 0.4648184032361037, :bias 0} {:key :time, :value 1.335902400676, :weight 0.43632873607404554, :bias 0}), :id "583c10bfdbd326ba34aed329139df6be2487ffc"} {:inputs ({:key :avolume, :value 2.25, :weight 0.13162215440222336, :bias 0} {:key :bvolume, :value 3.0, :weight 0.23886901184352727, :bias 0} {:key :ask, :value 1.32379, :weight 0.8408470512339872, :bias 0} {:key :bid, :value 1.3239, :weight 0.27071013797961796, :bias 0} {:key :time, :value 1.335902400676, :weight 0.6304505838898373, :bias 0}), :id "583c10bfdbd326ba34aed329139df6be2487ffd"} {:inputs ({:key :avolume, :value 2.25, :weight 0.8236972641966921, :bias 0} {:key :bvolume, :value 3.0, :weight 0.32421537754016705, :bias 0} {:key :ask, :value 1.32379, :weight 0.966306328543246, :bias 0} {:key :bid, :value 1.3239, :weight 0.8891668220470931, :bias 0} {:key :time, :value 1.335902400676, :weight 0.278993745549462, :bias 0}), :id "583c10bfdbd326ba34aed329139df6be2487ffe"} {:inputs ({:key :avolume, :value 2.25, :weight 0.27388486254027167, :bias 0} {:key :bvolume, :value 3.0, :weight 0.33659579299487363, :bias 0} {:key :ask, :value 1.32379, :weight 0.16610378593177033, :bias 0} {:key :bid, :value 1.3239, :weight 0.6964784902474896, :bias 0} {:key :time, :value 1.335902400676, :weight 0.6306732906337643, :bias 0}), :id "583c10bfdbd326ba34aed329139df6be2487fff"} {:inputs ({:key :avolume, :value 2.25, :weight 0.8819171698935051, :bias 0} {:key :bvolume, :value 3.0, :weight 0.5944805362120958, :bias 0} {:key :ask, :value 1.32379, :weight 0.9060962647355373, :bias 0} {:key :bid, :value 1.3239, :weight 0.37647418075176464, :bias 0} {:key :time, :value 1.335902400676, :weight 0.7797681719480866, :bias 0}), :id "583c10bfdbd326ba34aed329139df6be2488000"})
Thanks
Solution
I think part of zipper that creates new nodes is incorrect. If you read definition of zipper function, you'll see that make-node
is called with 2 arguments: current node, sequence of new children. Function should return new node.
Expression that you use #(%1)
shouldn't work at all, as you're trying to call map or list as a function.
Also expression (or map? list?)
doesn't really do what you want.
I adapted it for vectors just for the test.
(defn mk-zip [root] (let [branch? (fn [node] (when node (or (and (map? node) (contains? node :inputs)) (vector? node)))) children (fn [node] (cond (nil? node) nil (map? node) (:inputs node) :else node)) make-node (fn [node children] (cond (nil? node) nil (map? node) (assoc node :inputs children) (vector? node) (into [] children) :else node))] (zip/zipper branch? children make-node root))) (def root [{:inputs [{:key :avolume, :value 2.25, :weight 0.4283380545172636, :bias 0} {:key :bvolume, :value 3.0, :weight 0.6970037374879661, :bias 0} {:key :ask, :value 1.32379, :weight 0.5387437158323669, :bias 0} {:key :bid, :value 1.3239, :weight 0.4648184032361037, :bias 0} {:key :time, :value 1.335902400676, :weight 0.43632873607404554, :bias 0}], :id "583c10bfdbd326ba34aed329139df6be2487ffc"}])
Updating map items now works:
(def z (mk-zip root))
(-> z zip/next zip/down
(zip/insert-right {:key :new :value -10})
zip/up
zip/node)
Prints:
{:inputs ({:key :avolume, :weight 0.4283380545172636, :bias 0, :value 2.25} {:key :new, :value -10} {:key :bvolume, :weight 0.6970037374879661, :bias 0, :value 3.0} {:key :ask, :weight 0.5387437158323669, :bias 0, :value 1.32379} {:key :bid, :weight 0.4648184032361037, :bias 0, :value 1.3239} {:key :time, :weight 0.43632873607404554, :bias 0, :value 1.335902400676}), :id "583c10bfdbd326ba34aed329139df6be2487ffc"}