I'm writing a tree ( business process decision tree ) in clojure data structure .
(require clojure.zip :as z)
(z/vector-zip
[ :billed?
[:yes
[:check-bank-account]
[:check-cash] ]
[:send-out-email] ])
when code walk on the first node, it will read the keyword and perform certain actions, the result will be True or False, then I would like it walk into left
( True) or right
(False) node .
When my code starts with root node, and call some function associated with :billed?
it return a True
, how could clojure walk into :yes
node, or :send-out-email
node ? I thought there is only z/down
while, left
or right
is only for siblings not for directions of children.
Thank you very much for your time and appreciate any thoughts
Zippers walk through a data structure while keeping track of the current position. To reach different nodes, you have to apply a sequence of moves to the same zipper. When the zipper is created your position is directly above the tree:
(z/node tree)
=> [:billed? [:yes [:check-bank-account] [:check-cash]] [:send-out-email]]
So you can descend into the tree with z/down
, and use z/node
to get the current node from the zipper's location:
(-> tree
z/down
z/node)
=> :billed?
If you're walking the tree from the top towards some node, you probably only need z/down
and z/right
, since descending into a child vector will place you at the leftmost child. It's easier to imagine this if you lay out the vector in a flat line and imagine z/right
simply moving a cursor to the next element, and z/down
moving the cursor to inside a vector.
(-> tree
z/down
z/right
z/node)
=> [:yes [:check-bank-account] [:check-cash]]
(-> tree
z/down
z/right
z/right
z/node)
=> [:send-out-email]
Here's an example of you could walk this tree by evaluating the keys against a map of facts:
(def tree
(z/vector-zip
[:billed?
[:wire-funds?
[:check-bank-account]
[:check-cash]]
[:send-out-email]]))
(defn facts->action [facts]
(loop [curr (z/down tree)]
(let [node (z/node curr)]
(if-let [fact (find facts node)]
(if (val fact)
(recur (-> curr z/right z/down)) ;; descend "left"
(recur (-> curr z/right z/right z/down))) ;; descend "right"
node))))
(facts->action {:billed? false})
=> :send-out-email
(facts->action {:billed? true :wire-funds? true})
=> :check-bank-account
(facts->action {:billed? true :wire-funds? false})
=> :check-cash