📜  Scala中的递归

📅  最后修改于: 2022-05-13 01:55:12.580000             🧑  作者: Mango

Scala中的递归

递归是一种将问题分解为较小的子问题并为每个问题调用自身的方法。也就是说,它只是意味着函数调用自身。我们可以使用递归而不是循环。递归避免了与循环相关的可变状态。递归在函数式编程中很常见,它提供了一种描述许多算法的自然方式。递归被认为在函数式编程中很重要。 Scala 很好地支持递归。

让我们使用简单的阶乘示例来理解。
例子 :

// Scala program of factorial using recursion
  
// Creating object
object GFG
{
    // Function define
    def fact(n:Int): Int=
    {
        if(n == 1) 1
        else n * fact(n - 1)
    }
      
    // Main method
    def main(args:Array[String])
    {
        println(fact(3))
    }
}

输出:

6

上面的代码以递归方法演示了阶乘函数,其中条件n == 1导致递归中断。

让我们通过一个 gcd 的例子来更清楚地理解。
例子 :

// Scala program of GCD using recursion
  
// Creating object
object GFG
{
    // Function defined
    def gcd(x:Int, y:Int): Int=
    {
        if (y == 0) x
        else gcd(y, x % y)
    }
      
    // Main method
    def main(args:Array[String])
    {
        println(gcd(12, 18))
    }
}

输出:

6

递归的问题是,如果我们不小心,深度递归会炸毁堆栈。
让我们通过一个例子来理解这一点:
示例代码:

// Scala program of sum all numbers
// using recursion
   
// Creating object
object GFG
{
    // Function defined
    def sum(num: Int): Int=
    {
        if (num == 1)
            1
        else
            sum(num - 1) + num
    }
      
    // Main method
    def main(args:Array[String])
    {
        println(sum(55))
    }
}

输出:

1540

方法 sum 将对所有数字求和。我们每次都减少num并将其添加到结果中。在这里,每当我们调用 sum 时,它都会将输入值num留在堆栈上,并且每次都会占用内存。当我们尝试传递像 sum(555555) 这样的大输入时,输出将是Java .lang.StackOverflowError 。此输出意味着堆栈已被炸毁。

上面的示例没有使用尾递归,因此不是最佳方法,尤其是在起始值 n 非常大的情况下。

尾递归

尾递归函数被认为比非尾递归函数更好,因为尾递归可以由编译器优化。如果递归调用是函数完成的最后一件事,则称递归函数为尾递归。无需记录之前的状态。
让我们通过一个例子来理解它:

例子 :

// Scala program of factorial using tail recursion
import scala.annotation.tailrec
  
// Creating object
object GFG
{
    // Function defined
    def factorial(n: Int): Int =
    {
        // Using tail recursion
        @tailrec def factorialAcc(acc: Int, n: Int): Int =
        {
            if (n <= 1)
                acc
            else 
                factorialAcc(n * acc, n - 1)
        }
        factorialAcc(1, n)
    }
      
    // Main method
    def main(args:Array[String])
    {
        println(factorial(5))
    }
}

输出:

120

在上面的代码中,我们可以使用@tailrec注解来确认我们的算法是尾递归的。

如果我们使用这个注解并且我们的算法不是尾递归的,编译器就会报错。例如,如果我们尝试在上面的阶乘方法示例中使用此注解,我们将得到以下编译时错误。

例子 :

// Scala program of factorial with tail recursion
import scala.annotation.tailrec
  
// Creating object
object GFG
{
    // Function defined
    @tailrec def factorial(n: Int): Int =
    {
        if (n == 1)
            1
        else 
            n * factorial(n - 1)
    }
      
    // Main method
    def main(args:Array[String])
    {
        println(factorial(5))
    }
}

输出:

Could not optimize @tailrec annotated method factorial: it contains a recursive call not in tail position