Reputation: 25812
I am using Map
to implement pure functional
DFS
and BFS
for graph.
here is my code:
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 [];;
I know the code is quite long, but I really do wish for some suggestions:
functional
and hate imperative
now.functional
, personally I think the implementation I make seems more complicated than the imperative
array-everywhere version. Is my feeling correct?Edit
Added Bipartite
code
(* 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;;
Edit 2
DFS with list based path
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;;
Upvotes: 2
Views: 5798
Reputation: 66808
I'm not sure what you mean by worthy. It's worthy to set yourself this task as a learning exercise. It's also worthy to use immutable data to solve actual real world graph problems. It doesn't seem to me that graph processing is an area of application where pure functional code costs more than one is generally willing to pay for the benefits.
You're representing a path as a map from each node to the next. This is nice because you can start up the path in the middle. But a list is a simpler and more natural representation of a path for a lot of applications. At any rate, yours is a pretty heavyweight representation and so it makes your code a little heavier than I would have expected. (BTW it was hard to figure this out--some comments would help.)
I don't personally think this code is more complicated than imperative could would be. I also think that arrays make a poor representation for graphs when viewed as linked structures. So I don't believe an "arrays everywhere" solution is what you want to compare against. I'd compare against a malloc()/struct based (a la C) or against an object-based solution, personally.
When representing graphs as adjacency matrices, I'd say the array representation is more competitive. If your graph changes size a lot, or if you want to access nodes by keys other than integers, maps still have many advantages.
Upvotes: 3
Reputation: 361
It is worthy to do that if you cannot find good codes in open source community. Do not reinvent wheels.
There is another post has an extensive explanation on DFS algorithm by OCaml, Topological sort in OCaml What I suggest is to try write bfs, bfs_current and bfs_child into a single function.
Upvotes: 0