How to move zipper to left/right node in clojure?

155 Views Asked by At

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 rightis only for siblings not for directions of children.

Thank you very much for your time and appreciate any thoughts

2

There are 2 best solutions below

0
Taylor Wood On BEST ANSWER

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
0
exupero On

Chain multiple zipper movements.

Here's an example:

(require '[clojure.zip :as z])

(def zipper (z/vector-zip
              [:billed?
               [:yes
                [:check-bank-account]
                [:check-cash]]
               [:send-out-email]]))

(println (-> zipper z/down z/right z/down z/node))
(println (-> zipper z/down z/right z/down z/right z/down z/node))