大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
函数式编程
Scala 鼓励纯函数、不可变数据与显式错误建模。Either、Try 替代异常驱动流程;尾递归优化避免深递归栈溢出。对照 /roadmaps/scala「Functional Programming」节点。
学习目标
- 区分纯函数与副作用,理解引用透明性
- 使用
Either、Try表达失败路径 - 编写
@tailrec尾递归函数
核心概念
纯函数与不可变
def addAll(xs: List[Int]): Int =
xs.sum // 不修改 xs,无副作用
def withBonus(scores: Map[String, Int], user: String, bonus: Int): Map[String, Int] =
scores.updated(user, scores.getOrElse(user, 0) + bonus)
val base = Map("alice" -> 90)
val updated = withBonus(base, "alice", 5)
println(base("alice")) // 90 不变
println(updated("alice")) // 95
纯函数相同输入恒得相同输出;不可变数据结构通过 copy/updated 返回新实例。
Either 与 Try
def parseId(s: String): Either[String, Long] =
s.toLongOption.toRight(s"invalid id: $s")
def loadTitle(id: Long): Either[String, String] =
if id > 0 then Right(s"Bookmark #$id")
else Left(s"not found: $id")
def loadBookmark(rawId: String): Either[String, String] =
for
id <- parseId(rawId)
title <- loadTitle(id)
yield title
import scala.util.{Try, Success, Failure}
def safeDivide(a: Int, b: Int): Try[Double] =
Try(a.toDouble / b)
println(loadBookmark("42"))
println(loadBookmark("abc"))
println(safeDivide(10, 0))
Either 的 for comprehension 在 Left 时短路;Try 捕获非 fatal 异常,适合与 Java 互操作边界。
尾递归
import scala.annotation.tailrec
@tailrec
def factorial(n: Int, acc: BigInt = 1): BigInt =
if n <= 1 then acc
else factorial(n - 1, acc * n)
@tailrec
def findIndex[A](xs: List[A], pred: A => Boolean, idx: Int = 0): Int =
xs match
case Nil => -1
case h :: t if pred(h) => idx
case _ :: t => findIndex(t, pred, idx + 1)
println(factorial(20))
println(findIndex(List(1, 3, 5, 7), _ == 5))
@tailrec 要求递归调用在最后一行,编译器优化为循环;无注解的深递归可能 StackOverflowError。
与 Java 对照
| Scala | Java |
|---|---|
| 纯函数 + 不可变 | 可变 DTO 常见 |
Either[L, R] | 异常 / Result 类型(需库) |
Try[T] | try-catch |
@tailrec | 手动 while 循环 |
| for 推导短路 | 多层 flatMap |
Java 近年引入 Stream 与 Optional,但默认仍偏命令式;Scala 函数式是语言级习惯。
练习
- 用
Either实现链式校验:邮箱格式 + 非空标题 - 将递归
sum(List[Int])改写为@tailrec版本 - 对比 Java 抛异常与 Scala
Either在 API 设计上的利弊
小结
纯函数、Either/Try、尾递归是 Scala FP 的实战三角。 下一课类型系统与隐式。见 scala-essentials。