【发布时间】:2017-12-12 23:43:19
【问题描述】:
如何在 clojure 中编写等效于以下 python 的代码,但严格使用递归进行堆栈管理(例如,不使用循环/递归,向量作为边界)?
我意识到用一个矢量来保持你的路径并且只是偷看/弹出是相当简单的,但我这样做是作为一个思考练习。
Python 版本
def dfs(start,successors,goal,visited=set()):
if start not in visited:
visited.add(start)
for s in successors.get(start):
if goal(s):
return s
else:
res = dfs(s,successors)
if res: return res #bail early when found
return False
Clojure 版本
(defn dfs [start goal? successors visited]
(if (goal? start)
start
(when (not (contains? visited start))
(mapcat #(dfs % goal? successors (conj visited start))
(successors start)))))
由于在 Clojure 版本中迭代是由对 map 的调用控制的,因此您不能像在 Python 中那样提前放弃,例如if goal(s): return s。
由于您正在使用 map 收集列表内的递归调用,因此即使在找到目标之后,您也必须评估每个可能的节点。只有在探索完所有节点之后,您才能获得结果。
现在,我知道我可以做这样的事情(我知道这并不漂亮......只是想提供一个简单的例子,随时提出改进建议!)但我主要感兴趣的是看看是否有办法避免显式使用堆栈,并让调用堆栈像在 python 版本中那样工作。
(defn dfs-non-rec [frontier goal? successors visited]
(loop [f frontier g? goal? s successors v visited]
(let [node (peek f)]
(cond ; case 1
(goal? node)
node
;case 2
(not (contains? v node))
(recur (vec (concat (pop f) (successors node))) g? s (conj v node))
;case 3
:else
(recur (pop f) g? s (conj v node))))))
我应该如何处理这个问题?
编辑
对于所提供的某些答案是否实际上是深度优先的,我有些困惑。混乱源于我对输入的假设,我最初应该在这篇文章中提供。我假设输入是一个邻接列表,它代表一个 graph,而不是一个 tree
(def graph {"a" ["b","c","d"],
"b" ["a","e","f"],
"c" ["x","y"],
"d" [],
"e" [],
"f" [],
"x" ["c"],
"y" ["e"]})
然后,当转换为 seq 时,遵循的顺序实际上是深度优先 对于通过在图上调用 seq 创建的结果树,但是不遵循邻接列表隐含的顺序,因为图结构在转换中丢失了。
因此,如果您正在寻找从a 开始的节点x,我希望遍历顺序是adcyex,而不是abcdbaefcxy
【问题讨论】:
标签: python clojure depth-first-search