1. 引言
在本教程中,我们将深入探讨 Scala 中的 偏函数(Partial Function)。
✅ 偏函数是一种只对定义域的子集适用的函数。
举个例子:我们可以定义一个作用于 Int
类型的函数,但它只处理奇数。
2. 理解偏函数的定义
查看 Scala 官方文档,偏函数有以下三个关键特性:
- ✅ 是一元操作(只接受一个参数)
- ✅ 仅适用于其定义域的一个子集
- ✅ 可以显式定义
isDefinedAt
方法来判断是否适用,以及apply
方法来执行逻辑
来看一个简单的偏函数示例:
val squareRoot: PartialFunction[Double, Double] = {
def apply(x: Double) = Math.sqrt(x)
def isDefinedAt(x: Double) = x >= 0
}
这个函数具备如下特点:
- 接收一个
Double
类型参数 - 仅适用于非负数(
x >= 0
) - 显式定义了
isDefinedAt
和apply
方法
因此,它确实是一个偏函数。
⚠️ 实际开发中,isDefinedAt
和 apply
通常是隐式定义的。我们可以使用 case
语句来简化写法:
val squareRootImplicit: PartialFunction[Double, Double] = {
case x if x >= 0 => Math.sqrt(x)
}
如果我们对负数调用该函数,会抛出 scala.MatchError
运行时异常。
3. 使用 orElse
和 andThen
进行链式调用
偏函数的强大之处在于可以链式组合。
假设我们要实现一个函数:正数变负数,负数或零变正数。虽然直接乘以 -1
就行了,但为了演示链式调用,我们拆成两个偏函数:
val negativeOrZeroToPositive: PartialFunction[Int, Int] = {
case x if x <= 0 => Math.abs(x)
}
val positiveToNegative: PartialFunction[Int, Int] = {
case x if x > 0 => -1 * x
}
然后使用 orElse
将它们组合:
val swapSign: PartialFunction[Int, Int] = {
positiveToNegative orElse negativeOrZeroToPositive
}
此外,还可以使用 andThen
来串联后续处理逻辑。比如:
val printIfPositive: PartialFunction[Int, Unit] = {
case x if x > 0 => println(s"$x is positive!")
}
然后链式调用:
(swapSign andThen printIfPositive)(-1)
这样写出来的代码简洁清晰,逻辑一目了然。
4. 在集合中的使用
偏函数在处理集合时非常实用。主要有以下几个方法值得关注:
4.1. collect
、map
和 filter
✅ collect
方法会将偏函数应用到集合中满足条件的元素,并返回新集合。
定义一个偏函数用于 collect
:
val parseRange: PartialFunction[Any, Int] = {
case x: Int if x > 10 => x + 1
}
List(15, 3, "aString") collect { parseRange }
结果是 List(16)
,因为只有 15
满足类型为 Int
且大于 10。
⚠️ 如果我们用 map
:
List(15, 3, "aString") map { parseRange }
会抛出 scala.MatchError
,因为 map
会尝试对所有元素应用函数,包括字符串。
再看 filter
与 collect
的区别:
List(1, 2) collect { case i: Int => i > 10 }
List(1, 2) filter { case i: Int => i > 10 }
collect
返回:List(false, false)
filter
返回:List()
(空列表)
注意,这里我们用的是匿名函数形式定义的偏函数。
5. 总结
本文讲解了 Scala 中的偏函数,展示了如何通过 orElse
和 andThen
进行链式调用,以及如何在集合操作中使用 collect
、map
和 filter
。
✅ 偏函数在构建高可组合性代码时非常有用,尤其是在处理数据转换和集合操作时。
合理使用偏函数,可以让代码更加简洁、健壮、易读。
如需查看完整代码,欢迎访问:GitHub 项目地址