这个深度优先搜索实现现在是尾递归的吗?

COO*_*ANS 2 functional-programming scala tail-recursion fold

我有这个函数来功能性地遍历图形:

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
  }
Run Code Online (Sandbox Code Playgroud)

manuel kiessling 此处获得的实施

这很好,但我担心最后的“:+ 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
  }
Run Code Online (Sandbox Code Playgroud)

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

leftfold 是否已经是尾递归每次机会?

如果没有,还有另一种方法可以做我想做的事吗?

Bri*_*hon 7

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


Joe*_*e K 6

如果您想以尾递归方式实现 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)
}
Run Code Online (Sandbox Code Playgroud)

奖励:更改unvisited ++ restrest ++ unvisited,您将获得 BFS。