Scala递归函数(2)

69 阅读3分钟

Scala 中的递归函数是指在函数体内调用自身的函数,是函数式编程中处理循环逻辑的常用方式。以下是关于 Scala 递归函数的核心知识:

1. 基本定义与语法

递归函数的定义与普通函数类似,但需在函数体内调用自身,且必须明确指定返回值类型(Scala 编译器无法自动推断递归函数的返回类型)。

语法示例

scala

// 计算 n 的阶乘(n! = n × (n-1) × ... × 1)
def factorial(n: Int): Int = {
  if (n <= 1) 1  // 终止条件:递归的"出口"
  else n * factorial(n - 1)  // 递归调用:自身调用,参数逐步逼近终止条件
}

// 调用
println(factorial(5))  // 输出:120

2. 核心要素

递归函数必须满足两个条件,否则会导致无限递归(栈溢出):

  • 终止条件:存在一个或多个 base case,当满足条件时停止递归(如上述 n <= 1 时返回 1)。
  • 递归步骤:函数每次调用时,参数需逐步逼近终止条件(如 n 每次减 1)。

3. 尾递归(Tail Recursion)

普通递归可能因调用栈过深导致 StackOverflowError,而尾递归可避免此问题。尾递归指递归调用是函数体的最后一个操作(即递归调用的结果直接作为函数返回值,无需额外计算)。Scala 编译器会对尾递归进行优化,将其转换为循环,避免栈溢出。

示例:尾递归实现阶乘

scala

// 尾递归需要一个辅助函数保存中间结果
def factorialTail(n: Int): Int = {
  // 辅助函数:acc 用于累计结果
  def loop(current: Int, acc: Int): Int = {
    if (current <= 1) acc  // 终止条件:返回累计结果
    else loop(current - 1, current * acc)  // 尾递归调用:最后一步直接返回递归结果
  }
  loop(n, 1)  // 初始调用:累计值从 1 开始
}

println(factorialTail(10000))  // 不会栈溢出(普通递归此处会报错)

4. 尾递归检查

可通过 @annotation.tailrec 注解验证函数是否为尾递归,若不是则编译报错。

scala

import scala.annotation.tailrec

def sumTail(n: Int): Int = {
  @tailrec  // 验证是否为尾递归
  def loop(current: Int, acc: Int): Int = {
    if (current == 0) acc
    else loop(current - 1, current + acc)  // 尾递归:正确
  }
  loop(n, 0)
}

若函数不是尾递归(如 else n + sum(n-1)),添加 @tailrec 会编译失败。

5. 常见递归场景

  • 数学计算:阶乘、斐波那契数列(需优化为尾递归避免低效)、求和 / 乘积等。
  • 集合处理:遍历列表、树形结构(如二叉树的深度计算、遍历)。

示例:递归遍历列表求和

scala

def sumList(list: List[Int]): Int = list match {
  case Nil => 0  // 终止条件:空列表返回 0
  case head :: tail => head + sumList(tail)  // 递归:头元素 + 剩余列表的和
}

println(sumList(List(1, 2, 3, 4)))  // 输出:10

6. 注意事项

  • 栈溢出风险:非尾递归函数处理大数据时易栈溢出,优先使用尾递归或循环。
  • 性能:尾递归性能接近循环,普通递归因栈操作可能较慢。
  • 可读性:递归适合表达分治、树形结构等逻辑,复杂循环可用递归简化代码。

掌握递归(尤其是尾递归)是 Scala 函数式编程的基础,合理使用可写出简洁且高效的代码。