如何让Clojure拉链的一部分在深度优先遍历中已经访问过?
当您通过如何让Clojure拉链的一部分在深度优先遍历中已经访问过?,clojure,zipper,Clojure,Zipper,当您通过z/next以深度优先的方式迭代任意嵌套的拉链时,您能否获得或重建拉链的已访问部分,从而保留其结构?例如,让我们有一个向量拉链[0[12]3]。在访问1时,我如何实现visted功能以返回拉链的已访问部分,例如[0[1]] 编辑:在这些有用的答案的提示下,我意识到只有当它的子树被完全遍历时,loc才能被视为已访问。因此,只有非分支LOC(即(补码z/分支?)被视为已访问 (需要“[clojure.zip:as z]” (def拉链(z/vector拉链[0[12]3])) (defn访问
z/next
以深度优先的方式迭代任意嵌套的拉链时,您能否获得或重建拉链的已访问部分,从而保留其结构?例如,让我们有一个向量拉链[0[12]3]
。在访问1
时,我如何实现visted
功能以返回拉链的已访问部分,例如[0[1]]
编辑:在这些有用的答案的提示下,我意识到只有当它的子树被完全遍历时,loc
才能被视为已访问。因此,只有非分支LOC(即(补码z/分支?
)被视为已访问
(需要“[clojure.zip:as z]”
(def拉链(z/vector拉链[0[12]3]))
(defn访问
[拉链]
; ...
)
(->拉链z/下次访问)
; => [0]
(->拉链z/下一个z/下一次访问)
; => [0]
(->拉链z/下一个z/下一个z/下一次访问)
; => [0 [1]]
(->拉链z/下一个z/下一个z/下一个z/下一个已访问)
; => [0 [1 2]]
(->拉链z/下一个z/下一个z/下一个z/下一个z/下一个已访问)
; => [0 [1 2] 3]
z/lefts
只返回同一层次上访问的零件
编辑2:这似乎几乎奏效了。如果我们将其从开始改为,则它对示例拉链正确工作:
(已访问def)
(letfn[(根?[节点]
(=(z/节点)(z/根节点))]
(fn[节点]
(如果让[父节点(z/up节点)]
(让[comb fn(如果(根?父)为conj)]
(梳fn(访问过的家长)
(如果(z/分支?节点)
(矢量控制(z/lefts节点))
(conj(vec(z/lefts节点))(z/node节点()()()))
[])))) ;; 我们是根源
然而,随着嵌套的增加,它的局限性变得越来越明显。例如:
(定义拉链(z/vector-zip[0[1[2]]))
(->拉链z/下一个z/下一个z/下一个z/下一个z/下一个已访问)
; => [0 [1] [2]]
我想知道z/edit
是否更适合。不容易。您希望将值[0[1 2]]
作为输出,但从未访问过该值。因此,您不能仅仅通过查看以前访问过的任何节点来获得它。相反,您必须在检查访问者的历史记录的基础上,找到自己创建此结构的方法
这听起来不是不可能的,但算法并不完全显而易见。我的第一个反对意见是,这个问题似乎定义不清:您真的希望“已访问集”在访问时缩小吗?你说当访问<代码> [ 1 2 ] < /代码>时,你认为已经访问的部分是“代码> [0(1 2)] < /代码>,这意味着,当你查看矢量<代码> [1 2 ] < /代码>时,你认为它的所有内容都已经被访问过。在这种情况下,当您查看树根时,整个树已经被访问了,当您下降到树中时,访问量会越来越少
因此,我建议你对你想要的东西做出更准确的定义,如果你对它足够严格,我希望你能提出一个算法。使用拉链不是一个直接的答案,但你可以很容易地解决这个问题。以下是一个例子:
(dotest-focus ; walk the tree and keep track of all the visited nodes
(hid-count-reset)
(with-forest (new-forest)
; an "hid" is like a pointer to the node
(let [root-hid (add-tree-hiccup [:a
[:b
[:c 1]]
[:d
[:e 2]]])
tgt-hid (find-hid root-hid [:** :c]) ; hid of the :c node we want to stop at
state-atom (atom {:visited-hids []
:found-tgt? false})
enter-fn (fn [path] ; path is from root
(let [curr-hid (xlast path)] ; curr node is last elem in path
(swap! state-atom
(fn [curr-state]
(cond-it-> curr-state
(falsey? (grab :found-tgt? it)) (update it :visited-hids append curr-hid)
(= curr-hid tgt-hid) (assoc it :found-tgt? true))))))]
(newline)
(println "Overall Tree Structure:")
(spy-pretty (hid->tree root-hid))
(newline)
(walk-tree root-hid {:enter enter-fn}) ; accum results => state atom
(newline)
(println "final state map")
(spyx @state-atom)
(newline)
(let [depth-first-tags (it-> (grab :visited-hids @state-atom)
(mapv hid->node it)
(mapv #(grab :tag %) it))]
(is= depth-first-tags [:a :b :c])
(println "depth-first tags thru target:")
(println depth-first-tags)
(newline)))))
输出:
Overall Tree Structure:
{:tag :a,
:tupelo.forest/kids
[{:tag :b,
:tupelo.forest/kids [{:tupelo.forest/kids [], :tag :c, :value 1}]}
{:tag :d,
:tupelo.forest/kids [{:tupelo.forest/kids [], :tag :e, :value 2}]}]}
final state map
(clojure.core/deref state) => {:visited-hids [1005 1002 1001], :found-tgt? true}
depth-first tags thru target:
[:a :b :c]
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
根据您的具体用例,您可以根据需要调整输出格式。您的评论“z/lefts只返回相同层次级别上访问的部分”表明了它自己的解决方案。调用z/lefts,然后使用z/up提升一个级别,然后递归地继续
我画了一个不完全正确的草图,但接近正确的形状,并说明了我的建议:
(defn visited [node]
(let [parent (z/up node)]
(if (nil? parent)
[] ;; we're at the root
(conj (visited parent)
(if (z/branch? node)
(vec (z/lefts node))
(conj (vec (z/lefts node)) (z/node node)))))))
如果我正确理解你的目标,我相信这会奏效:
(defn visited [loc]
(loop [cur loc
start true]
;; Loop from the starting location up to the root
(if-let [par (z/up cur)]
(recur
;; Replace our parent with a node that only includes its visited children
(z/replace
par
(z/make-node
par
(z/node par)
(cond-> (z/lefts cur)
;; If we started at a branch, don't treat its children as visited
(not (and start (z/branch? cur))) (concat [(z/node cur)]))))
false)
(if (and start (not (z/end? cur)))
[]
(z/node cur)))))
作为测试,下面是一个示例,用于为相对复杂树的遍历中的每个步骤打印来自访问的的返回:
(def root (z/vector-zip [[1 [2 3 4] 5] 6 [[[7 8] [9]] 10 11]]))
(loop [loc root]
(when (not (z/end? loc))
(println "visit " (z/node loc))
(println "visited " (visited loc))
(recur (z/next loc))))
visit [[1 [2 3 4] 5] 6 [[[7 8] [9]] 10 11]]
visited []
visit [1 [2 3 4] 5]
visited []
visit 1
visited [[1]]
visit [2 3 4]
visited [[1]]
visit 2
visited [[1 [2]]]
visit 3
visited [[1 [2 3]]]
visit 4
visited [[1 [2 3 4]]]
visit 5
visited [[1 [2 3 4] 5]]
visit 6
visited [[1 [2 3 4] 5] 6]
visit [[[7 8] [9]] 10 11]
visited [[1 [2 3 4] 5] 6]
visit [[7 8] [9]]
visited [[1 [2 3 4] 5] 6 []]
visit [7 8]
visited [[1 [2 3 4] 5] 6 [[]]]
visit 7
visited [[1 [2 3 4] 5] 6 [[[7]]]]
visit 8
visited [[1 [2 3 4] 5] 6 [[[7 8]]]]
visit [9]
visited [[1 [2 3 4] 5] 6 [[[7 8]]]]
visit 9
visited [[1 [2 3 4] 5] 6 [[[7 8] [9]]]]
visit 10
visited [[1 [2 3 4] 5] 6 [[[7 8] [9]] 10]]
visit 11
visited [[1 [2 3 4] 5] 6 [[[7 8] [9]] 10 11]]
nil
谢谢你抓住了我问题的拙劣表述!事实上,我想要的是只为非分支LOC获取访问的部分。我不认为一个分支可以被视为被访问,除非它的子树被完全遍历。如果我读对了,它不会保留被访问树的结构。有几种方法可以用来输出被截断的树。你能描述一下这个问题的动机吗?例如,它可以用来显示网页的一部分(即Hiccup),直到给定的点。这几乎是有效的。如果我们以开始,改为而不是conj
(参见上面我编辑的问题),它将正确地遍历示例拉链。但是,如果使用更多嵌套的数据结构,则会失败。