3Scala for the Impatient(第2版)

函数式编程

FP基础

高阶函数闭包柯里化Option

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第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))  // 15

3.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 有什么区别?

如何实现一个自定义的高阶函数?


3/7函数式编
0%