tailrec 注解
保证优化
tailrec 注解 是 CoddyKit 上的免费 Scala for Backend Engineering & Functional Programming 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Scala for Backend Engineering & Functional Programming 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。
什么是尾递归
当递归调用是函数执行的最后一个动作时,它处于尾部位置。尾递归函数可以被优化为循环,重复使用同一个栈帧,因此不会发生栈溢出。
尾部位置
在 n * factorial(n-1) 中,递归调用不是最后一个动作:它返回后还要执行乘法。在 gcd(b, a % b) 中,调用是最后一个动作。只有后者是尾递归。
@tailrec 注解
导入 scala.annotation.tailrec 并为方法添加注解。编译器随后会验证该调用确实处于尾部位置,并应用优化。如果不是,编译就会失败。
import scala.annotation.tailrec
object Main {
@tailrec
def countdown(n: Int): Unit = {
if (n >= 0) {
println(n)
countdown(n - 1)
}
}
def main(args: Array[String]): Unit = countdown(3)
}有保证的优化
@tailrec 的关键优势是编译时保证。如果您的函数不具备栈安全性,您会立即得到提示,而不是等到大输入导致运行时崩溃后才发现问题。
尾递归 gcd
欧几里得算法本身就是尾递归的:递归调用就是函数体的完整结果。为它添加注解即可确认这一点。
import scala.annotation.tailrec
object Main {
@tailrec
def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
def main(args: Array[String]): Unit = {
println(gcd(1071, 462))
}
}哪些情况会破坏尾部位置
以下常见模式会使调用离开尾部位置:
- 对结果执行算术运算:
n + f(...)。 - 将结果包装在构造器中:
x :: f(...)。 - 在
try代码块中使用结果。
非尾递归示例
这个求和函数不是尾递归的,因为加法包装了递归调用。为它添加 @tailrec 会导致编译错误。(示例未添加注解,因此可以运行。)
object Main {
def sum(n: Int): Int =
if (n == 0) 0
else n + sum(n - 1)
def main(args: Array[String]): Unit = {
println(sum(100))
}
}为什么无法优化
因为 n + sum(n - 1) 必须记住 n,以便调用返回后完成加法,所以每一层都需要自己的栈帧。编译器无法将其压缩为循环,因此它不是尾递归。
大型尾递归循环
使用累加器的尾递归求和可以处理极大的输入而不发生溢出,因为它会重复使用一个栈帧。
import scala.annotation.tailrec
object Main {
@tailrec
def sumTo(n: Int, acc: Long = 0): Long =
if (n == 0) acc else sumTo(n - 1, acc + n)
def main(args: Array[String]): Unit = {
println(sumTo(1000000))
}
}tailrec 要求 final 或局部方法
要使 @tailrec 生效,该方法不能被重写:它必须是 private、final,或局部/嵌套方法。开放方法可能被重写,从而破坏优化,因此编译器会拒绝它。
相互递归的注意事项
@tailrec 只会优化调用自身的函数。两个函数相互调用(相互递归)时,JVM 无法直接进行尾部优化;这种情况需要蹦床机制,稍后会介绍。
快速检查
检验您对 @tailrec 的理解。
回顾
您学习了@tailrec 注解:
- 处于尾部位置的调用可以被优化为循环。
@tailrec为栈安全性提供编译时保证。- 方法必须是
final、private或局部方法。 - 它只适用于自身递归,不适用于相互递归。
常见问题解答
「tailrec 注解」课时是免费的吗?
是的 — 「tailrec 注解」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Scala for Backend Engineering & Functional Programming 课程的其余内容,请升级到 CoddyKit PRO。 Scala for Backend Engineering & Functional Programming 课程共包含 4 节课。
「tailrec 注解」这节课中我会学到什么?
保证优化 你通过在浏览器中直接运行的动手代码来练习 Scala for Backend Engineering & Functional Programming,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Scala for Backend Engineering & Functional Programming 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Scala for Backend Engineering & Functional Programming 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「tailrec 注解」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Scala for Backend Engineering & Functional Programming 课中编写并运行代码吗?
能。每节 Scala for Backend Engineering & Functional Programming 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。