图中的DFS和BFS在OCaml中以**纯**函数方式

时间:2013-04-05 13:31:22

标签: graph functional-programming ocaml

我正在使用Map为图表实施pure functional DFSBFS

这是我的代码:

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 [];;

我知道代码很长,但我真的希望得到一些建议:

  1. 是否值得真正实现一套纯函数图算法?我这样做是因为我已经习惯了functional并且现在讨厌imperative
  2. 我的实施在某些部分或全部都太复杂了吗?
  3. 虽然我喜欢functional,但我个人认为我所做的实现似乎比imperative数组无处不在的版本更复杂。我的感觉是否正确?

  4. 修改

    添加了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;;
    

2 个答案:

答案 0 :(得分:3)

我不确定你的意思是什么。将自己的这项任务作为一项学习练习是值得的。使用不可变数据来解决实际的真实图形问题也是值得的。在我看来,图形处理是一个应用领域,其中纯粹的功能代码成本不止一个通常愿意支付利益。

您将路径表示为从每个节点到下一个节点的地图。这很好,因为你可以在中间启动路径。但是列表是许多应用程序的路径的更简单和更自然的表示。无论如何,你的代表是非常重量级的代表,因此它使你的代码比我预期的要重一点。 (顺便说一句,这很难说清楚 - 有些评论会有所帮助。)

我个人认为这段代码比命令式更复杂。我还认为,当被视为链接结构时,数组对图形的表示很差。所以我不相信你想要比较的“阵列无处不在”解决方案。我将基于malloc()/ struct(a la C)或基于对象的解决方案进行比较。

当将图表表示为邻接矩阵时,我会说数组表示更具竞争力。如果您的图表大小变化很大,或者您想要通过整数以外的键访问节点,那么地图仍然有许多优点。

答案 1 :(得分:0)

  1. 如果您在开源社区找不到合适的代码,那么值得这样做。不要重新发明轮子。

  2. 还有另一篇文章对OCaml的{DFS算法'进行了广泛的解释,Topological sort in OCaml 我建议尝试将bfs,bfs_current和bfs_child写入单个函数。