我正在尝试使用DFS遍历图表。
但是当我尝试将访问节点列表作为函数的参数传递时,我发现存在问题。
当我到达除了前一个节点之外没有连接节点的节点时,递归调用结束,有关被访问节点的信息消失,因此陷入无限循环......
除了使用命令式方法之外,有没有办法保留有关被访问节点的信息?
答案 0 :(得分:3)
阐述杰弗里的答案,你有几种不同的风格。我在这里只给出了我尚未测试的片段,因此可能存在小错误或大错误。
您可以在任何地方使用副作用:
module NodeSet = Set.Make(...)
let traverse action graph_root =
let visited = ref NodeSet.empty in
let rec loop node =
action node;
visited := NodeSet.add node !visited;
let handle child =
if not (NodeSet.mem child !visited)
then loop acc child in
List.iter handle (children node)
in loop graph_root
"访问"将命令函数action
应用于所有节点
图表。
您可以将受访节点存储在可变引用中,但可以存储在线程中
作为累加器acc
而不是遍历的遍历状态
直接测序副作用。这将对应于使用
国家monad。
let traverse action init_state graph_root =
let visited = ref NodeSet.empty in
let rec loop acc node =
let acc = action acc node in
visited := NodeSet.add node !visited;
let handle acc child =
if NodeSet.mem child !visited
then acc
else loop acc child in
List.fold_left handle acc (children node)
in loop init_state graph_root
您可以重复使用此状态传递逻辑来传递被访问者 节点信息。
let traverse action init_state graph_root =
let rec loop acc visited node =
let acc = action acc node in
let visited = NodeSet.add node visited in
let handle (acc, visited) child =
if NodeSet.mem child !visited
then (acc, visited)
else loop acc visited child in
List.fold_left handle (acc, visited) (children node)
in loop init_state NodeSet.empty graph_root
最后,您可以通过传递进行尾递归遍历 有关哪个节点应在第一个节点中接下来计算的信息 递归调用。这对应于一般的转变 继续传递样式,但具有特定于域的表示 延续(只是访问的节点)。
let traverse action init_state graph_root =
let rec loop acc visited = function
| [] -> acc
| node::to_visit ->
if NodeSet.mem node visited then loop acc visited to_visit
else begin
let acc = action acc node in
let visited = NodeSet.add node visited in
let to_visit = children node @ to_visit in
loop acc visited to_visit
end
in loop NodeSet.empty init_state [graph_root]
Jeffrey评论说,通过此演示文稿,您可以更改
通过简单地改变to_visit
的方式从DFS到BFS的遍历顺序
更新,将子节点添加到序列的末尾
而不是在开头(这需要一个队列结构
算法效率高。)
答案 1 :(得分:2)
一种看待这种情况的方法是你想要前进来尝试图中其他可能的节点,而不是返回(就像你要做的那样,遍历一棵树)。您可以使用参数来描述您访问过的节点以及您计划访问的节点。 to-visit参数最初只是第一个节点。每次到达新节点时,都会将任何未访问的相邻节点(通过查看访问节点集可以判断)添加到未访问的节点集,并以递归方式继续这种方式。那么,DFS和BFS之间的区别在于您订购要访问的节点列表的方式。
在函数式编程中,有很多次,而不是从函数返回,而是调用函数来做下一件事。 (这就是尾递归有时很重要的原因。)