第3章:函数式编程
一、导读
1.1 本章学习目标
第3章讲解 Scala 的函数式编程:高阶函数、闭包、柯里化、Option。
本章核心学习目标:
- 掌握高阶函数:函数作为参数和返回值
- 理解闭包:函数记住环境
- 掌握柯里化
- 理解 Option:避免 null
1.2 为什么本章如此重要
函数式编程是 Scala 的灵魂。高阶函数、不可变数据、模式匹配是 Scala 代码的核心特征。
1.3 核心问题引导
什么是高阶函数?
柯里化有什么用途?
Option 如何替代 null?
二、核心概念详解
3.1 高阶函数
scala
// 函数作为参数
def applyTwice(f: Int => Int, x: Int): Int = f(f(x))
println(applyTwice(x => x * 2, 3)) // 12
// 常用高阶函数
val nums = List(1, 2, 3, 4, 5)
nums.map(_ * 2) // List(2, 4, 6, 8, 10)
nums.filter(_ > 3) // List(4, 5)
nums.foldLeft(0)(_ + _) // 15
nums.reduce(_ * _) // 120
// 函数作为返回值
def multiplier(factor: Int): Int => Int = {
(x: Int) => x * factor
}
val double = multiplier(2)
val triple = multiplier(3)
println(double(5)) // 10
println(triple(5)) // 153.2 闭包
scala
// 闭包: 函数引用外部变量
var more = 10
val addMore = (x: Int) => x + more
println(addMore(5)) // 15
more = 20
println(addMore(5)) // 25 (闭包捕获了 more 的引用)
// 闭包在集合操作中很常见
val factor = 3
val scaled = nums.map(_ * factor)3.3 柯里化
scala
// 柯里化: 将多参数函数转为一系列单参数函数
def add(x: Int)(y: Int): Int = x + y
println(add(3)(4)) // 7
val add3 = add(3) _ // 部分应用
println(add3(4)) // 7
// 实用示例
def withLogging[A](label: String)(body: => A): A = {
println(s"Starting: $label")
val result = body
println(s"Done: $label")
result
}
withLogging("database query") {
// 执行数据库操作
Thread.sleep(100)
"result"
}3.4 Option
scala
// Option 替代 null
def findUser(id: Int): Option[String] = {
if (id > 0) Some(s"User$id") else None
}
val user = findUser(1)
user match {
case Some(name) => println(s"Found: $name")
case None => println("Not found")
}
// Option 操作
val name = findUser(1).getOrElse("Anonymous")
val upper = findUser(1).map(_.toUpperCase)
val len = findUser(1).map(_.length).getOrElse(0)
// for 推导式
val result = for {
u <- findUser(1)
if u.startsWith("User")
} yield u.length
// Option 链式调用
def getUserEmail(id: Int): Option[String] = {
findUser(id).flatMap(u => getEmail(u))
}三、实战案例
3.1 案例一:函数式数据处理
scala
case class Employee(name: String, department: String, salary: Double)
val employees = List(
Employee("Alice", "Engineering", 80000),
Employee("Bob", "Marketing", 60000),
Employee("Charlie", "Engineering", 90000),
Employee("David", "Marketing", 65000),
Employee("Eve", "Engineering", 85000)
)
// 按部门分组
val byDept = employees.groupBy(_.department)
// 每个部门的平均薪资
val avgSalary = byDept.map { case (dept, emps) =>
dept -> emps.map(_.salary).sum / emps.size
}
// 薪资最高的前 3 名
val top3 = employees.sortBy(-_.salary).take(3)
// 工程部门的总薪资
val engTotal = employees.filter(_.department == "Engineering").map(_.salary).sum
println(s"Engineering total: $$${engTotal}")
top3.foreach(e => println(s"${e.name}: $$${e.salary}"))四、本章小结
高阶函数让函数成为一等公民
柯里化支持部分应用和 DSL 构建
Option 安全地处理可能缺失的值
五、思考与练习
map 和 flatMap 有什么区别?
如何实现一个自定义的高阶函数?