【问题标题】:Is this Depth First Search implementation tail recursive now?现在这个深度优先搜索实现是尾递归的吗?
【发布时间】:2019-08-17 00:24:08
【问题描述】:

我有这个函数来功能遍历一个图:

private def dfs(current: RCell, rCellsMovedWithEdges: Vector[RCell], acc: Vector[RCell] = Vector()): Vector[RCell] = {
    current.edges.foldLeft(acc) {
      (results, next) =>
        if (results.contains(rCellsMovedWithEdges(next))) results
        else dfs(rCellsMovedWithEdges(next), rCellsMovedWithEdges, results :+ current)
    } :+ current
  }

实现取自manuel kiessling here

这很好,但我担心最后的“:+ current”会使其成为非尾递归。

我改成这样了:

private def dfs(current: RCell, rCellsMovedWithEdges: Vector[RCell]): Vector[RCell] = {

    @annotation.tailrec
    def go(current: RCell, rCellsMovedWithEdges: Vector[RCell], acc: Vector[RCell] = Vector()): Vector[RCell] = {
      current.edges.foldLeft(acc) {
        (results, next) =>
          if (results.contains(rCellsMovedWithEdges(next))) results
          else go(rCellsMovedWithEdges(next), rCellsMovedWithEdges, results :+ current)
      }
    }
    go(current, rCellsMovedWithEdges) :+ current
  }

但编译器说递归调用不在尾部位置。

leftfold 是否已经尾递归了?

如果没有,还有其他方法可以做我想做的事吗?

【问题讨论】:

    标签: scala functional-programming tail-recursion fold


    【解决方案1】:

    它不是尾递归,因为最后一次调用不是go,而是foldLeft。它甚至不可能是相互尾递归的,因为foldLeft 多次调用go。很难使 DFS 尾递归,因为递归算法严重依赖调用堆栈来跟踪您在树中的位置。如果你能保证你的树很浅,我建议不要打扰。否则,您将需要传递一个显式堆栈(List 是一个不错的选择)并完全重写您的代码。

    【讨论】:

      【解决方案2】:

      如果你想实现 DFS 尾递归,你必须手动管理堆栈:

      def dfs(start: RCell, rCellsMovedWithEdges: Vector[RCell]): Vector[RCell] = {
        @annotation.tailrec
        def go(stack: List[RCell], visited: Set[RCell], acc: Vector[RCell]): Vector[RCell] = stack match {
          case Nil => acc
          case head :: rest => {
            if (visited.contains(head)) {
              go(rest, visited, acc)
            } else {
              val expanded = head.edges.map(rCellsMovedWithEdges)
              val unvisited = expanded.filterNot(visited.contains)
              go(unvisited ++ rest, visited + head, acc :+ head)
            }
          }
        }
        go(List(start), Set.empty, Vector.empty)
      }
      

      奖励:将 unvisited ++ rest 更改为 rest ++ unvisited 即可获得 BFS。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多