Scala 递归和尾递归

本文探讨Scala编程中的递归和尾递归概念。递归通过定义函数自身来解决问题,需包含结束条件并逐步接近解决方案。而尾递归在Scala中得到优化,避免栈溢出,其特点是递归调用为函数的最后一项操作,通常配合累加器使用,以实现迭代的效果。Java则未对尾递归进行优化。

Scala 递归和尾递归

1、递归

1.1、找到递归函数
              f(5) = 5 *f(4-1)
              f(n) = n *f(n-1)
2.1、在递归函数内部,一定要有结束条件
3.1、随着递归的深入,要有机会到达结束条件

object RecursiveDemo1 {
  def main(args: Array[String]): Unit = {
    println(factorial(5))
  }

  /**
    * 计算n的阶层
    *
    */
  def factorial(n:Long):Long = {
    if(n==1) 1
    else n * factorial(n-1)
  }

2、尾递归

在 scala 中对尾递归做了优化,就不会出现栈溢出

在递归的时候,只有递归,没有任何其他运算,这就是尾递归

scala 做了优化(迭代算法),java 没有

要找到一个合适的累加器

走到最深层的时候,结果出来之后,也是最后的结果

object RecursiveDemo1 {
  def main(args: Array[String]): Unit = {
    println(factorial01(5,acc=1))
  }

  /**
    * 计算n的阶层
    *
    */
  def factorial(n:Long):Long = {
    if(n==1) 1
    else n * factorial(n-1) // 这不是尾递归,有 n ,与递归平级的不能有其他运算
  }
  //累加器:用来储存中间结果 :acc
  @tailrec //加上这个代码若不是尾递归会报错
  def factorial01(n:Int,acc:BigInt):BigInt={
    if(n==1)  acc
    else factorial01(n-1,n*acc) //这是尾递归,除了递归外没有其他运算
  }
}
### 单向递归尾递归区别及实现方式 单向递归(通常指普通的递归尾递归(Tail Recursion)是两种不同的递归形式,它们在递归调用的位置执行方式上存在显著差异。 #### 单向递归 单向递归是一种常见的递归形式,其中递归调用通常出现在函数的中间或末尾,但在递归调用之后可能还有其他操作需要执行。这种递归形式无法直接通过编译器优化来减少调用栈的开销,因此在递归深度较大时容易导致栈溢出。 以经典的斐波那契数列为例,传统的单向递归实现如下: ```java public static int FibonacciRecursively(int n) { if (n < 2) return n; return FibonacciRecursively(n - 1) + FibonacciRecursively(n - 2); } ``` 此实现方式需要多次调用自身,并且在每次递归调用返回后仍需进行加法运算,因此不能进行尾调用优化[^1]。 #### 尾递归 尾递归是一种特殊的递归形式,其中递归调用是函数中的最后一个操作,并且递归调用的结果直接返回给上层调用者,不需要在调用后执行其他操作。由于这一特性,尾递归可以被编译器优化为循环结构,从而避免栈溢出问题并提高性能。 以斐波那契数列为例子,尾递归的实现方式如下: ```java public static int FibonacciTailRecursively(int n, int a, int b) { if (n == 0) return a; if (n == 1) return b; return FibonacciTailRecursively(n - 1, b, a + b); } ``` 在这个实现中,`FibonacciTailRecursively` 函数的最后一行仅调用自身,没有额外的计算操作。因此,编译器可以优化该递归调用,将其转换为循环迭代,从而减少调用栈的使用[^1]。 #### 应用场景 单向递归适用于逻辑较为复杂、递归调用后需要进一步处理的场景,但需要注意递归深度较大时可能导致栈溢出问题。尾递归则适用于递归调用后无需进一步操作的场景,能够通过优化减少内存开销,适用于深度递归任务。 在实际编程中,如果语言支持尾递归优化(如Scala、Haskell等),推荐使用尾递归来提高程序性能。然而,对于不支持尾递归优化的语言(如Java),可以通过手动转换为循环结构来模拟尾递归行为。 #### 总结 单向递归尾递归的主要区别在于递归调用是否是函数的最后一个操作。单向递归递归调用后可能还需要执行其他操作,而尾递归则直接返回递归调用的结果。尾递归可以通过编译器优化减少调用栈的开销,提高程序性能。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值