ocaml图形遍历:如何保持访问的节点?

ocaml图形遍历:如何保持访问的节点?,ocaml,Ocaml,我正在尝试用DFS遍历一个图 但当我试图将访问的节点列表作为函数的参数传递时,我发现有一个问题 当我到达除前一个节点外没有连接节点的节点时,递归调用结束,有关访问节点的信息消失,从而陷入无限循环 除了使用命令式方法外,还有什么方法可以保存有关已访问节点的信息吗?一种方法是,您希望继续尝试图中其他可能的节点,而不是返回(例如,遍历树)。您可以有一些参数,不仅描述您访问过的节点,还描述您计划访问的节点。tovisit参数最初只是第一个节点。每次到达新节点时,都会将任何未访问的相邻节点(通过查看已访问

我正在尝试用DFS遍历一个图

但当我试图将访问的节点列表作为函数的参数传递时,我发现有一个问题

当我到达除前一个节点外没有连接节点的节点时,递归调用结束,有关访问节点的信息消失,从而陷入无限循环


除了使用命令式方法外,还有什么方法可以保存有关已访问节点的信息吗?

一种方法是,您希望继续尝试图中其他可能的节点,而不是返回(例如,遍历树)。您可以有一些参数,不仅描述您访问过的节点,还描述您计划访问的节点。tovisit参数最初只是第一个节点。每次到达新节点时,都会将任何未访问的相邻节点(通过查看已访问的节点集可以看出)添加到未访问的节点集中,并以这种方式递归继续。那么,DFS和BFS之间的区别在于您对要访问的节点列表的排序方式


在函数式编程中,很多时候,您不是从函数返回,而是调用函数来执行下一步操作。(这就是为什么尾部递归有时很重要。)

看待这一点的一种方法是,您希望继续尝试图中其他可能的节点,而不是返回(例如,遍历树)。您可以有一些参数,不仅描述您访问过的节点,还描述您计划访问的节点。tovisit参数最初只是第一个节点。每次到达新节点时,都会将任何未访问的相邻节点(通过查看已访问的节点集可以看出)添加到未访问的节点集中,并以这种方式递归继续。那么,DFS和BFS之间的区别在于您对要访问的节点列表的排序方式


在函数式编程中,很多时候,您不是从函数返回,而是调用函数来执行下一步操作。(这就是为什么尾部递归有时很重要。)

详细阐述Jeffrey的答案,您可以使用几种不同的样式。我在这里只给出我没有测试过的片段,所以可能会有小错误或大错误

  • 您可以在任何地方使用副作用:

    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
    
    “访问”将命令式功能
    操作
    应用于中的所有节点 图表

  • 您可以将访问的节点存储在可变引用中,但线程 作为累加器的遍历状态
    acc
    而不是 直接排序副作用。这将对应于使用 国家一号

    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
    
  • 最后,您可以通过传递移动到尾部递归遍历 关于在第一个过程中接下来应计算哪些节点的信息 递归调用。这对应于到的一般转换 延续传递样式,但具有特定于域的表示形式 连续性(只需访问节点)

    Jeffrey指出,通过本演示,您可以更改 从DFS到BFS的遍历顺序,只需将
    的方式更改为_visit
    更新,将子节点添加到序列的末尾,而不是 而不是在开始时(这需要创建队列结构) 算法有效)


  • 详细阐述杰弗里的答案,你有几种不同的风格可供选择。我在这里只给出我没有测试过的片段,所以可能会有小错误或大错误

  • 您可以在任何地方使用副作用:

    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
    
    “访问”将命令式功能
    操作
    应用于中的所有节点 图表

  • 您可以将访问的节点存储在可变引用中,但线程 作为累加器的遍历状态
    acc
    而不是 直接排序副作用。这将对应于使用 国家一号

    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
    
  • 最后,您可以通过传递移动到尾部递归遍历 关于在第一个过程中接下来应计算哪些节点的信息 递归调用。这对应于到的一般转换 延续传递样式,但具有特定于域的表示形式 连续性(只需访问节点)

    Jeffrey指出,通过本演示,您可以更改 从DFS到BFS的遍历顺序,只需将
    的方式更改为_visit
    更新,将子节点添加到序列的末尾,而不是 而不是在开始时(这需要创建队列结构) 算法有效)


  • 我不知道OCAML,但你能传递一个包含访问节点的集合吗?我是OCAML的新手,但OCAML是面向值的语言,所以一旦设置了变量的值,就没有办法改变值,除非使用命令式方式,这在函数式编程中不是首选方式……在Clojure(函数式)中,当你添加到一个集合时,你有一套新的。语言中有一些结构支持这一概念。我感谢你的评论。我会继续查找我不知道OCAML,但你能传递一个包含访问节点的集合吗?我是OCAML的新手,但OCAML是面向值的语言,所以一旦设置了变量的值,就没有办法更改值,除非使用命令式方式,这在函数式编程中不是首选方式……在Clojure(functional)中,当你添加到一个集合时,你会得到一个新的集合。语言中有一些结构支持这一概念。我感谢你的评论。我将继续查找4.,您是否应该过滤掉已访问的节点?如
    let to_visit=children node@to_visit |>List.remove_if(flip NodeSet.mem visted)in…
    well-spoted@unhammer,谢谢。我已经更改了代码,以检查在访问之前是否已经访问过,而不是