偏函数 #

一、偏函数概述 #

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 结合了 filtermap

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