clojurefunctional-programmingzipper

Clojure zipper to remove all right siblings


I want to write a function for a zipper that removes all right siblings of a node while staying at the same location.

(defn remove-all-rights-1 [loc]
  (if (zip/right loc) 
    (recur (zip/remove (zip/right loc)))
    loc))

The problem here is that remove returns the location that would have preceded the current node in DFS.

Therefore the following example...

(-> (clojure.zip/vector-zip [1 [[2] 3]])
    (zip/down)
    (zip/right)
    (zip/down)
    (remove-all-rights-1)
    (zip/replace :x)
    (zip/root))

...gives [1 [[:x]]] instead of [1 [:x]] because zip/remove jumped to the bottom leaf instead of just going back left.

How should I remove the right siblings without also changing location in the tree? Thanks in advance!


Solution

  • (letfn [(kill-right [loc]
                (let [lost   (zip/rights loc)
                      parent (-> loc zip/up zip/node)
                      node   (into (empty parent) (take (- (count parent) (count lost)) parent))]
                    (-> loc
                        zip/up
                        (zip/replace node)
                        zip/down
                        zip/rightmost)))]
        (-> (clojure.zip/vector-zip [1 [[2] 3]])
            zip/down
            zip/right
            zip/down
            kill-right
            (zip/replace :x)
            zip/root))