EDIT #2: This entire question and exploration were based on my missing the fundamental notion of zippers; that they represent a perspective in a datastructure from the point of view of a particular node. So a zipper is - at all times - a pair of the current node and what the rest of the tree looks like from the perspective of that node. I was originally trying to generate a whole new structure from the zipper, while the zipper itself was all I needed, all along. I'll leave this all up for posterity, in the hope that somebody else is helped by it (or so it serves as a warning to any successors!).
Original question:
I'm trying to get my head around using zippers to manipulate trees. The specific problem is that I need to generate at runtime routes between two nodes matching arbitrary criteria in an arbitrary tree.
I thought I could use the path
function to get a route to a location by calling path
on the current location. But the path returned seems to omit the last step(s) required to get there.
For example:
(def test-zip (vector-zip [0 [1] [2 [3 [4] 5 [6 [7] [8]]]]]))
(-> test-zip
down right right down
right down right right
node)
gives 5
, but
(-> test-zip
down right right down
right down right right
path)
gives
[[0 [1] [2 [3 [4] 5 [6 [7] [8]]]]] [2 [3 [4] 5 [6 [7] [8]]]] [3 [4] 5 [6 [7] [8]]]]
which isn't the same location (it's missing the effect of the last three steps, down right right
).
It looks like the path function only gets you to the parent location in the tree, ignoring any siblings between you and the actual location.
Am I missing the point of the path
function? I'd assumed that given a tree and a path, applying the path to the tree would bring you to the original location of the path, not partly there.
UPDATE: I've used the following function definition to compile a path of nodes from a start location to an end location:
(defn lca-path [start-loc end-loc]
(let [sczip (z/root start-loc)
start-path (z/path start-loc)
start-node (z/node start-loc)
end-path (z/path end-loc)
end-node (z/node end-loc)
lca-path (filter (set start-path) end-path)
lca-node [(last lca-path)]
lca-to-start (conj (vec (drop (count lca-path) start-path)) start-node)
lca-to-end (conj (vec (drop (count lca-path) end-path)) end-node)
]
(concat (reverse lca-to-start) lca-node lca-to-end))
)
Pretty heavily influenced by the chat with @Mark Fisher, thanks!