(defn walk-along-and-do
  "Follow along the path from the given loc. When the path is contained
  invoke the found-action. Otherwise invoke the not-found-action with
  the loc of the last contained node as well as the not contained path
  components. pred is used to identify the nodes."
  [loc pred p found-action not-found-action]
  ; Get rid of special case: the empty path. This makes pc in
  ; the loop always non-nil.
  (if-let p (seq p)
    (loop [loc              loc
           [pc & pcs :as p] p]
      (let [is-equal (pred (zip/node loc) pc)]
        (cond
          ; No match? Try the next node.
          (and (not is-equal)
               (zip/right loc)) (recur (zip/right loc) p)

          ; No match!
          ; XXX: Report the parent and the original path!
          (not is-equal)        (not-found-action (zip/up loc) p)

          ; XXX: From here on the node matches!
          ; In case we have nothing left in the path, we
          ; found the target node.
          (nil? pcs)            (found-action loc)

          ; For branch go on for the children and the rest of
          ; the path's components.
          (and (zip/branch? loc)
               (zip/down loc))  (recur (zip/down loc) pcs)

          ; In any other case the path is not contained in the tree.
          :else                 (not-found-action loc pcs)))
      (found-action loc))))

(defn walk-along
  "Follow along the path from the given loc. In case the path is not
  contained in the zipper, an exception is thrown. The empty path is
  always contained and leaves the loc as is. pred is used to identify
  the nodes."
  [loc pred p]
  (walk-along-and-do loc pred p identity
                     (fn [_ _] (throw (new Exception "path not in tree")))))

(defn try-walk-along
  "Try to follow along the path from the given loc. In case the path
  is not contained in the zipper, the last contained location and the
  rest of the path are returned in a vector. The empty path is always
  contained and leaves the loc as is. pred is used to identify the
  nodes."
  [loc pred p]
  (walk-along-and-do loc pred p (fn [loc] [loc nil])
                     (fn [loc rpath] [loc rpath])))

