偏函数 #
一、偏函数概述 #
1.1 什么是偏函数 #
偏函数(PartialFunction)是只对部分输入值有定义的函数。对于没有定义的输入值,调用会抛出异常。
1.2 与普通函数的区别 #
- 普通函数:对所有输入都有定义
- 偏函数:只对部分输入有定义
二、定义偏函数 #
2.1 使用 PartialFunction 特质 #
scala
val divide: PartialFunction[Int, Int] = new PartialFunction[Int, Int] {
def apply(x: Int): Int = 10 / x
def isDefinedAt(x: Int): Boolean = x != 0
}
divide.isDefinedAt(2)
divide.isDefinedAt(0)
divide(2)
divide(0)
2.2 使用模式匹配语法 #
scala
val divide: PartialFunction[Int, Int] = {
case d if d != 0 => 10 / d
}
divide.isDefinedAt(2)
divide.isDefinedAt(0)
divide(2)
2.3 简单示例 #
scala
val positive: PartialFunction[Int, String] = {
case x if x > 0 => s"positive: $x"
}
val evenOnly: PartialFunction[Int, String] = {
case x if x % 2 == 0 => s"even: $x"
}
三、偏函数方法 #
3.1 isDefinedAt #
检查输入是否有定义:
scala
val sqrt: PartialFunction[Int, Double] = {
case x if x >= 0 => math.sqrt(x)
}
sqrt.isDefinedAt(4)
sqrt.isDefinedAt(-1)
sqrt.isDefinedAt(0)
3.2 applyOrElse #
安全调用,提供默认值:
scala
val divide: PartialFunction[Int, Int] = {
case d if d != 0 => 10 / d
}
divide.applyOrElse(2, (_: Int) => -1)
divide.applyOrElse(0, (_: Int) => -1)
3.3 lift #
将偏函数转换为返回 Option 的函数:
scala
val divide: PartialFunction[Int, Int] = {
case d if d != 0 => 10 / d
}
val divideLifted = divide.lift
divideLifted(2)
divideLifted(0)
3.4 orElse #
组合两个偏函数:
scala
val even: PartialFunction[Int, String] = {
case x if x % 2 == 0 => s"even: $x"
}
val odd: PartialFunction[Int, String] = {
case x if x % 2 != 0 => s"odd: $x"
}
val number = even.orElse(odd)
number(2)
number(3)
number.isDefinedAt(0)
3.5 andThen #
组合偏函数与普通函数:
scala
val even: PartialFunction[Int, Int] = {
case x if x % 2 == 0 => x
}
val doubled = even.andThen(_ * 2)
doubled(4)
doubled.isDefinedAt(3)
四、collect 方法 #
4.1 collect 与 filter + map #
collect 结合了 filter 和 map:
scala
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val evenStrings = numbers.collect {
case x if x % 2 == 0 => s"even: $x"
}
val evenStrings2 = numbers.filter(_ % 2 == 0).map(x => s"even: $x")
4.2 安全提取 #
scala
val mixed: List[Any] = List(1, "hello", 2, "world", 3, 3.14)
val ints = mixed.collect { case i: Int => i }
val strings = mixed.collect { case s: String => s }
println(ints)
println(strings)
4.3 复杂模式匹配 #
scala
case class Person(name: String, age: Int)
case class Company(name: String, employees: Int)
val entities = List(
Person("Alice", 25),
Company("Acme", 100),
Person("Bob", 30),
Company("Globex", 50)
)
val adults = entities.collect {
case p @ Person(_, age) if age >= 18 => p
}
val companies = entities.collect {
case c @ Company(name, _) if name.startsWith("A") => c
}
五、偏函数实战 #
5.1 JSON 解析 #
scala
val parseValue: PartialFunction[String, Any] = {
case s if s == "true" || s == "false" => s.toBoolean
case s if s.matches("-?\\d+") => s.toInt
case s if s.matches("-?\\d+\\.\\d+") => s.toDouble
case s if s.startsWith("\"") && s.endsWith("\"") => s.substring(1, s.length - 1)
}
val values = List("42", "3.14", "true", "\"hello\"", "invalid")
val parsed = values.collect(parseValue)
5.2 命令处理 #
scala
sealed trait Command
case class Add(item: String) extends Command
case class Remove(item: String) extends Command
case object Clear extends Command
case object List extends Command
val parseCommand: PartialFunction[String, Command] = {
case s"add $item" => Add(item)
case s"remove $item" => Remove(item)
case "clear" => Clear
case "list" => List
}
def handleCommand: PartialFunction[Command, String] = {
case Add(item) => s"Added: $item"
case Remove(item) => s"Removed: $item"
case Clear => "Cleared all items"
case List => "Listing items..."
}
val processCommand = parseCommand.andThen(handleCommand)
processCommand("add apple")
processCommand("list")
5.3 错误处理 #
scala
sealed trait Result[+T]
case class Success[T](value: T) extends Result[T]
case class Failure(message: String) extends Result[Nothing]
val handleResult: PartialFunction[Result[Int], String] = {
case Success(value) if value > 0 => s"Positive: $value"
case Success(value) if value < 0 => s"Negative: $value"
case Success(0) => "Zero"
case Failure(msg) => s"Error: $msg"
}
val results = List(Success(10), Success(-5), Success(0), Failure("error"))
results.map(handleResult)
5.4 事件处理 #
scala
sealed trait Event
case class Click(x: Int, y: Int) extends Event
case class KeyPress(key: Char) extends Event
case class Scroll(delta: Int) extends Event
val handleEvent: PartialFunction[Event, String] = {
case Click(x, y) if x > 0 && y > 0 => s"Clicked in first quadrant at ($x, $y)"
case Click(x, y) => s"Clicked at ($x, $y)"
case KeyPress(k) if k.isLetter => s"Letter pressed: $k"
case KeyPress(k) => s"Key pressed: $k"
case Scroll(d) if d > 0 => "Scrolled up"
case Scroll(d) => "Scrolled down"
}
val events = List(
Click(100, 200),
Click(-10, 50),
KeyPress('a'),
KeyPress('1'),
Scroll(10),
Scroll(-5)
)
events.collect(handleEvent)
六、偏函数组合 #
6.1 链式 orElse #
scala
val handleA: PartialFunction[String, String] = {
case "a" => "A"
}
val handleB: PartialFunction[String, String] = {
case "b" => "B"
}
val handleC: PartialFunction[String, String] = {
case "c" => "C"
}
val handler = handleA.orElse(handleB).orElse(handleC)
handler("a")
handler("b")
handler("c")
handler.isDefinedAt("d")
6.2 动态组合 #
scala
def createHandler(keys: Set[String]): PartialFunction[String, String] =
keys.map { key =>
val pf: PartialFunction[String, String] = { case `key` => s"Handled: $key" }
pf
}.reduce(_ orElse _)
val handler = createHandler(Set("a", "b", "c"))
handler("a")
handler("b")
handler.isDefinedAt("d")
6.3 andThen 链 #
scala
val parse: PartialFunction[String, Int] = {
case s if s.matches("\\d+") => s.toInt
}
val validate: Int => Int = n => if n > 0 then n else throw new IllegalArgumentException
val process = parse.andThen(validate).andThen(_ * 2)
process("10")
process.lift("-5")
七、偏函数与 Option #
7.1 lift 转换 #
scala
val pf: PartialFunction[Int, String] = {
case x if x > 0 => s"positive: $x"
}
val lifted = pf.lift
lifted(5)
lifted(-5)
7.2 从 Option 创建偏函数 #
scala
def fromOption[A, B](f: A => Option[B]): PartialFunction[A, B] = {
case x if f(x).isDefined => f(x).get
}
val parseInt = fromOption((s: String) => scala.util.Try(s.toInt).toOption)
parseInt("42")
parseInt.isDefinedAt("hello")
八、偏函数最佳实践 #
8.1 使用 collect 替代 filter + map #
scala
val result1 = list.filter(isValid).map(transform)
val result2 = list.collect {
case x if isValid(x) => transform(x)
}
8.2 使用 orElse 处理默认情况 #
scala
val handler = specificHandler.orElse {
case _ => "default response"
}
8.3 使用 lift 安全调用 #
scala
val safeResult = pf.lift(input).getOrElse(defaultValue)
8.4 模式匹配偏函数 #
scala
val handler: PartialFunction[Event, Unit] = {
case Click(x, y) => handleClick(x, y)
case KeyPress(key) => handleKey(key)
case _ => ()
}
九、总结 #
偏函数方法 #
| 方法 | 说明 |
|---|---|
| isDefinedAt | 检查是否有定义 |
| applyOrElse | 安全调用 |
| lift | 转为 Option 函数 |
| orElse | 组合偏函数 |
| andThen | 与普通函数组合 |
使用场景 #
| 场景 | 方法 |
|---|---|
| 安全提取 | collect |
| 命令处理 | orElse |
| 事件处理 | 模式匹配 |
| 错误处理 | lift |
下一步,让我们学习 类与对象!
最后更新:2026-03-27