我正在使用Map
为图表实施pure functional
DFS
和BFS
。
这是我的代码:
module IntMap = Map.Make(struct type t = int let compare = compare end);;
module IntSet = Set.Make(struct type t = int let compare = compare end);;
type digraph = int list IntMap.t;;
exception CantAddEdge;;
let create v =
let rec fill i acc =
if i < v then
fill (i+1) (IntMap.add i [] acc)
else
acc
in
fill 0 IntMap.empty;;
let num_vertices g = IntMap.cardinal g;;
let add_edge u v g =
if IntMap.mem u g && IntMap.mem v g then
let add u v g =
let l = IntMap.find u g in
if List.mem v l then g
else IntMap.add u (v::l) g
in
add u v (add v u g)
else
raise CantAddEdge;;
let dfs_path u g =
let rec dfs current visited path =
let dfs_child current (visited, path) c =
if not (IntSet.mem c visited) then
dfs c (IntSet.add c visited) (IntMap.add c current path)
else
(visited, path)
in
List.fold_left (dfs_child current) (visited, path) (IntMap.find current g)
in
let (v, p) = dfs u (IntSet.singleton u) IntMap.empty
in
p;;
let bfs_path u g =
let rec bfs current_list v p n =
let bfs_current (v,p,n) current =
let bfs_child current (v, p, n) c =
if not (IntSet.mem c v) then begin
print_int c;
((IntSet.add c v), (IntMap.add c current p), (c::n))
end
else
(v, p, n)
in
List.fold_left (bfs_child current) (v, p, n) (IntMap.find current g)
in
let (v,p,n) = List.fold_left bfs_current (v,p,n) current_list
in
if n = [] then p
else bfs n v p []
in
bfs [u] (IntSet.singleton u) IntMap.empty [];;
我知道代码很长,但我真的希望得到一些建议:
functional
并且现在讨厌imperative
。functional
,但我个人认为我所做的实现似乎比imperative
数组无处不在的版本更复杂。我的感觉是否正确?修改
添加了Bipartite
代码
(* basically, we have two sets, one for red node and the other for black node*)
(* we keep marking color to nodes via DFS and different level of nodes go to coresponding color set*)
(* unless a node is meant to be one color but already in the set of the other color*)
type colorType = Red | Black;;
let dfs_bipartite u g =
let rec dfs current color red black block =
if block then (red, black, block)
else
let dfs_child current color (red, black, block) c =
if block then (red, black, block)
else
let c_red = IntSet.mem c red and c_black = IntSet.mem c black in
if (not c_red) && (not c_black) then
if color = Red then
dfs c Black (IntSet.add c red) black false
else
dfs c Red red (IntSet.add c black) false
else if (c_red && color = Black) || (c_black && color = Red) then (red, black, true)
else (red, black, block)
in
List.fold_left (dfs_child current color) (red, black, block) (IntMap.find current g)
in
let (r, b, block) = dfs u Black (IntSet.singleton u) IntSet.empty false
in
not block;;
修改2
带有基于列表的路径的DFS
let dfs_path u g =
let rec dfs current visited path =
let dfs_child (visited, path) c =
if not (IntSet.mem c visited) then begin
print_int c;
dfs c (IntSet.add c visited) (c::path)
end
else (visited, path)
in
List.fold_left dfs_child (visited, path) (IntMap.find current g)
in
let (v, p) = dfs u (IntSet.singleton u) [u]
in
p;;
答案 0 :(得分:3)
我不确定你的意思是什么。将自己的这项任务作为一项学习练习是值得的。使用不可变数据来解决实际的真实图形问题也是值得的。在我看来,图形处理是一个应用领域,其中纯粹的功能代码成本不止一个通常愿意支付利益。
您将路径表示为从每个节点到下一个节点的地图。这很好,因为你可以在中间启动路径。但是列表是许多应用程序的路径的更简单和更自然的表示。无论如何,你的代表是非常重量级的代表,因此它使你的代码比我预期的要重一点。 (顺便说一句,这很难说清楚 - 有些评论会有所帮助。)
我个人认为这段代码比命令式更复杂。我还认为,当被视为链接结构时,数组对图形的表示很差。所以我不相信你想要比较的“阵列无处不在”解决方案。我将基于malloc()/ struct(a la C)或基于对象的解决方案进行比较。
当将图表表示为邻接矩阵时,我会说数组表示更具竞争力。如果您的图表大小变化很大,或者您想要通过整数以外的键访问节点,那么地图仍然有许多优点。
答案 1 :(得分:0)
如果您在开源社区找不到合适的代码,那么值得这样做。不要重新发明轮子。
还有另一篇文章对OCaml的{DFS算法'进行了广泛的解释,Topological sort in OCaml 我建议尝试将bfs,bfs_current和bfs_child写入单个函数。