【问题标题】:Tail recursion - Scala (any language else)尾递归 - Scala(其他任何语言)
【发布时间】:2019-02-16 21:35:14
【问题描述】:

我有一个关于尾递归的问题。据我所知,尾递归是函数的最后一次递归调用将传递函数的结果。但是当我有这样的功能时

def func1(n: Int): Int = {
   if (n > 100) {
      n - 10
   }
   else {
      func1(func1(n + 11))
   }
}

会是尾递归吗?例如

func1(100) = func1(func1(111)) = func1(101) = 91

所以最后一个递归调用将是 func1(101) 并且它应该提供结果,所以这将是尾递归,对吗?我有点困惑。谢谢!

【问题讨论】:

    标签: scala recursion tail-recursion


    【解决方案1】:

    它不是尾递归的。您可以将代码重写为如下所示:

    def func1(n: Int): Int = {
       if (n > 100) {
          n - 10
       }
       else {
          val f = func1(n + 11)
          func1(f)
       }
    }
    

    可以看到第 6 行有一个对 func1 的调用,它不在尾部位置。

    【讨论】:

    • 谢谢!但为什么第 6 行的 func1 不在尾部位置?如果它再次调用该函数,它不应该也处于尾部位置并调用另一个函数,如第 7 行中的函数吗?
    • 这段代码不是尾递归的。 val f = func1(n + 11) - 在这里您可以深入调用堆栈。
    • 啊,我明白为什么不是这样了。因为在它完成并返回值之后,仍然必须再次调用该函数,并将输入作为该值。谢谢你们!
    【解决方案2】:

    任何简单的检查方法都是尝试一下。如果你的方法不是尾递归的,@tailrec 注释(import scala.annotation.tailrec)会给你一个编译时错误。

    这不是尾递归,因为您在非尾位置进行了递归调用。

    你的函数中有两个递归调用,一个在尾部位置,它是方法中的最后一个调用,但另一个是该调用的输入,它不是尾部递归,因为它后面有东西,下一个电话。在尾部位置有一个递归调用是不够的,每个递归调用都必须是一个尾部调用

    【讨论】:

      【解决方案3】:

      不,您的示例不是尾递归的情况。

      func1(func1(n + 11)) 是一种非线性递归(尤其是嵌套递归)。

      尾递归是线性递归的一种特殊情况,可以立即转换为迭代(循环),这就是为什么它很有趣,因为它可以轻松优化。

      在您的情况下,对内部函数的调用不是函数中的最后一个操作(仍然等待对外部函数的调用),因此,它不是尾递归。

      【讨论】:

      • 所以递归必须首先是线性递归才能成为尾递归?
      • 是的,尾递归是线性递归的一种特殊情况。在尾递归中,递归调用在最后,这意味着只有一个递归调用(这意味着线性递归)
      【解决方案4】:

      实际上尾递归方法是一种“返回”结果本身或下一次调用的方法。如果您可以通过以下方式重写您的算法 - 它可以是尾递归的。

      trait Recursive[R] {
        def oneIteration: Either[R, Recursive[R]]
      }
      
      object Recursive {
        def interpret[R](fn: Recursive[R]): R = {
          var res: Either[R, Recursive[R]] = Right(fn)
          while(res.isRight) {
            res = res.right.get.oneIteration
          }
          res.left.get
        }
      }
      
      object Factorial extends App {
        def factorial(acc: BigDecimal, n: Int): Recursive[BigDecimal] = new Recursive[BigDecimal] {
          override def oneIteration(): Either[BigDecimal, Recursive[BigDecimal]] = {
            if(n == 1 ){
              Left(acc)
            }
            else {
              Right(factorial(acc * n, n - 1))
            }
          }
        }
      
        val res = Recursive.interpret(factorial(1 , 5))
        println(res)
      }
      

      【讨论】:

      • 这段代码很酷。但它使用while 并使用var。不确定这里是否相关。
      • 它只是展示了尾递归是如何工作的。无需在生产中使用它)我使用varwhile 而不是tailrec 来展示如何在没有语言支持的情况下模拟尾递归。
      猜你喜欢
      • 2018-03-17
      • 2018-03-08
      • 1970-01-01
      • 1970-01-01
      • 2022-10-06
      • 1970-01-01
      • 2017-04-10
      • 1970-01-01
      • 2015-06-10
      相关资源
      最近更新 更多