简体   繁体   English

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

[英]Convert normal recursion to tail recursion with multiple recursive calls

I am trying to understand how various recursive functions can be converted to tail recursive. 我试图了解如何将各种递归函数转换为尾递归。 I've looked through the many examples of both fibonacci and factorial conversions to tail recursive and understand those but am having a tough time making the leap to a problem with a somewhat different structure. 我已经查看了斐波纳契和阶乘转换的许多例子,以便尾递归并理解这些,但我很难在一个有点不同结构的问题上跳跃。 An example is: 一个例子是:

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

How would you convert this to a tail recursive implementation? 你会如何将其转换为尾递归实现?

I've looked through similar questions such as: Convert normal recursion to tail recursion But again these don't seem to translate to this problem. 我已经查看了类似的问题,例如: 将正常递归转换为尾递归但是这些似乎并没有转化为这个问题。

Some things just are not tail recursive, and any attempt to transform them will end up building the stack manually. 有些事情就是不能尾递归,任何试图改变他们最终将手动构建堆栈。

In this case, however, we can accumulate (untested, don't have scala): 但是,在这种情况下,我们可以累积(未经测试,没有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)
}

The trick to turning your function, or any function that requires multiple calls to itself into a tail recursive function is to break down the multiple calls into a list of arguments that can be recursively consumed and applied: 将函数或任何需要多次调用自身的函数转换为尾递归函数的技巧是将多个调用分解为可递归使用和应用的参数列表:

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)
}

The inner function countSteps2 no longer takes a single argument but a list of arguments and an accumulator. 内部函数countSteps2不再采用单个参数,而是采用参数列表和累加器。 This way we can calculate the value for the head of the arguments or generate a new list of arguments than can be added to the existing sequence to have countSteps2 recurse on. 这样我们就可以计算参数头部的值,或者生成一个新的参数列表,而不是可以添加到现有序列中以使countSteps2递归。

Each time we do have an input, we take the head and compute either a 0, 1 or additional list of arguments. 每次我们确实有输入时,我们采取head并计算0,1或其他参数列表。 Now we can just recurse on countSteps2 again with the new list of arguments prepended to the existing tail and any value we did calculate added to the accumulator, acc . 现在我们可以再次使用现有tail前面的新参数列表以及我们计算的任何值添加到累加器acc再次递归countSteps2 Since the only call to countSteps2 is an exit condition, the recursion is a tail recursion 由于对countSteps2的唯一调用是退出条件,因此递归是尾递归

We can finally exit when all inputs have been consumed, at which time acc has the results of all the recursive steps summed in it. 当所有输入都被消耗时,我们终于可以退出,此时acc具有所有递归步骤的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM