函数式编程
纯函数、Either/Try、尾递归。
35 分钟
函数式编程
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 常见 |