1.4. 函数式编程

Scala 的函数式编程特性使其能够编写简洁、可组合且线程安全的代码。

1.4.1. 函数定义

1.4.1.1. 方法定义

// 基本函数
def add(x: Int, y: Int): Int = x + y

// 单表达式函数可省略大括号
def square(x: Int): Int = x * x

// 多行函数体
def greet(name: String): String =
  val msg = s"Hello, $name"
  msg.toUpperCase

// 无参数函数
def now(): Long = System.currentTimeMillis()
def pi: Double = 3.14159        // 省略 () 表示每次调用重新求值

1.4.1.2. 过程(无返回值)

def log(msg: String): Unit = println(s"[LOG] $msg")
// 或省略返回类型声明
def log(msg: String): Unit = println(s"[LOG] $msg")

1.4.2. 高阶函数

高阶函数是**接受函数作为参数**或**返回函数**的函数:

// 接受函数参数
def operate(a: Int, b: Int, f: (Int, Int) => Int): Int = f(a, b)

val sum    = operate(3, 4, _ + _)       // 7
val product = operate(3, 4, _ * _)      // 12
val diff   = operate(3, 4, (x, y) => x - y)  // -1
// 返回函数(闭包)
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

1.4.2.1. 匿名函数(Lambda)

// 完整写法
val f1: (Int, Int) => Int = (x: Int, y: Int) => x + y

// 类型推断简写
val f2 = (x: Int, y: Int) => x + y

// 占位符语法(每个参数只出现一次时)
val f3: (Int, Int) => Int = _ + _

警告

占位符语法简洁但易读性下降。建议在简单表达式中使用,复杂逻辑使用显式参数名。

1.4.3. 纯函数与副作用

纯函数 满足: 1. 相同的输入始终产生相同的输出 2. 不产生副作用(I/O、修改状态、抛出异常)

// 纯函数
def pureAdd(a: Int, b: Int): Int = a + b

// 不纯函数(依赖外部状态)
var counter = 0
def impureAdd(x: Int): Int =
  counter += 1
  x + counter

重要

纯函数易于测试、推理和并行化。尽量将副作用推到程序边界。

1.4.4. 不可变性

不可变对象没有可变状态,天然线程安全:

// 可变(不推荐)
import scala.collection.mutable
val buf = mutable.ListBuffer(1, 2, 3)
buf += 4

// 不可变(推荐)
val list = List(1, 2, 3)
val newList = list :+ 4    // 返回新 List,原 List 不变

1.4.5. Option、Either 与 Try

1.4.5.1. Option

表示可能存在或不存在的值,替代 null:

def findUser(id: Int): Option[String] =
  if id == 1 then Some("Alice")
  else None

// 安全访问
findUser(1).getOrElse("Unknown")      // "Alice"
findUser(99).getOrElse("Unknown")     // "Unknown"

// 映射
findUser(1).map(_.toUpperCase)        // Some("ALICE")
findUser(99).map(_.toUpperCase)       // None

// 模式匹配
findUser(1) match
  case Some(name) => println(s"找到: $name")
  case None       => println("未找到")

1.4.5.2. Either

表示两个可能的结果:成功(Right)或失败(Left):

def divide(a: Int, b: Int): Either[String, Int] =
  if b == 0 then Left("除数不能为零")
  else Right(a / b)

divide(10, 2) match
  case Right(result) => println(s"结果: $result")
  case Left(error)   => println(s"错误: $error")

// 链式调用(Right-biased)
divide(10, 2).map(_ * 2)   // Right(10)

1.4.5.3. Try

专门用于捕获异常的函数式构造:

import scala.util.{Try, Success, Failure}

def riskyDivide(a: Int, b: Int): Try[Int] = Try(a / b)

riskyDivide(10, 2) match
  case Success(v) => println(s"成功: $v")
  case Failure(e) => println(s"失败: ${e.getMessage}")

// 链式操作
riskyDivide(10, 2)
  .map(_ * 2)
  .getOrElse(0)

1.4.6. Monad 基础概念

Monad 是函数式编程中的核心设计模式,Scala 中的 Option、List、Either、Try 等都是 Monad。一个 Monad 必须满足:

  1. unit (也称 pure / apply):将一个值包装到 Monad 上下文中

  2. flatMap (也称 bind / >>=):将 Monad 中的值通过函数转换,保持上下文

// flatMap 链式操作(避免层层嵌套)
def parseInt(s: String): Option[Int] =
  s.toIntOption

def sqrt(i: Int): Option[Double] =
  if i >= 0 then Some(math.sqrt(i.toDouble)) else None

val result = parseInt("16")
  .flatMap(sqrt)

// for 推导式(Monad 的语法糖)
val result2 = for
  i <- parseInt("16")
  r <- sqrt(i)
yield r
// result2: Some(4.0)

小技巧

for 推导式本质是 flatMap + map 的语法糖。 学习识别模式:每个 <- 对应一次 flatMap,最后的 yield 对应 map。