使用多个递归调用将正常递归转换为尾递归

时间:2015-05-07 22:51:22

标签: algorithm scala recursion tail-recursion

我试图理解各种递归函数如何转换为尾递归。我已经查看了斐波纳契和阶乘转换的许多例子,以便尾递归并理解这些,但我很难在一个有点不同结构的问题上跳跃。一个例子是:

def countSteps(n: Int): Int = {
  if(n<0) return 0
  if(n==0) return 1
  countSteps(n-1) + countSteps(n-2) + countSteps(n-3)
}

您如何将其转换为尾递归实现?

我查看了类似的问题,例如: Convert normal recursion to tail recursion 但这些似乎并没有转化为这个问题。

2 个答案:

答案 0 :(得分:4)

有些事情 不是尾递归的,任何转换它们的尝试都会最终手动构建堆栈。

但是,在这种情况下,我们可以累积(未经测试,不要使用scala):

def countSteps(n: Int): Int = {
  if (n < 0) return 0
  countStepsAux(n, 0, 1, 0, 0)
}

def countStepsAux(n: Int, step: Int, n1: Int, n2: Int, n3: Int): Int = {
  if (n == step) return n1
  countStepsAux(n, step + 1, n1 + n2 + n3, n1, n2)
}

答案 1 :(得分:3)

将函数或任何需要多次调用自身的函数转换为尾递归函数的技巧是将多个调用分解为可递归使用和应用的参数列表:

def countSteps(n: Int): Int = {
  def countSteps2(steps: List[Int], acc: Int): Int =
    steps match {
      case Nil => acc
      case head :: tail =>
        val (n, s) = if (head < 0) (0, Nil)
        else if (head == 0) (1, Nil)
        else (0, List(head - 1, head - 2, head - 3))
        countSteps2(s ::: tail, n + acc)
    }
  countSteps2(List(n),0)
}

内部函数countSteps2不再需要一个参数,而是一个参数列表和一个累加器。这样我们就可以计算参数头部的值,或者生成一个新的参数列表,而不是可以添加到现有序列中以使countSteps2递归。

每次我们输入时,我们取head并计算0,1或其他参数列表。现在,我们可以使用现有countSteps2前面的新参数列表再次递归tail,并将我们计算的任何值添加到累加器acc。由于对countSteps2的唯一调用是退出条件,因此递归是尾递归

我们最终可以在消耗完所有输入后退出,此时acc将所有递归步骤的结果汇总在其中。