scala中有PartialFunction的概念, 同时还要一个概念叫Partial Applied Function. 前者译作偏函数, 后者译作"偏应用函数"或"部分应用函数", 一字之差, 差距很大.
1.偏函数
首先我们介绍样本序列(Case sequences),从而具体介绍什么是偏函数。样本序列是一种特殊的函数,通常情况下函数只有一个入口,但是样本序列会有多个函数入口。比如下面的列子:
val withDefault: Option[Int] => Int = {
case Some(x) => x
case None => 0
}
withDefault是一个(Option[Int] => Int)类型的方法,这个方法体包含2个case.第一个匹配Some类型,第二个匹配None类型
scala> withDefault(Some(10))
res28: Int = 10
scala> withDefault(None)
res29: Int = 0
这种方法的使用在Akka的actor中经常使用到,比如actor中的receive方法
def receive = {
case Data(byte) =>
sum += byte
case GetChecksum(requester) =>
val checksum = ~(sum & 0xFF) + 1
requester ! checksum
}
如果,你传递的参数值是case里面无法匹配的,它将会产生一个运行时的异常,比如
val second: List[Int] => Int = {
case x :: y :: _ => y
}
scala> second(List())
scala.MatchError: List()
at $anonfun$1.apply(<console>:17)
at $anonfun$1.apply(<console>:17)
注意此时的second还仅是一个函数类型,具体来说它是一个function1类型 ,如果你想定义一个偏函数函数必须进行如下的声明 (function1和PartialFunction的区别稍后讲解)
val second2: PartialFunction[List[Int],Int] = {
case x :: y :: _ => y
}
事实上,这个字面函数
{ case x :: y :: _ => y }在编译时会翻译成下面的语句,但是如果你的类型声明是Funtion1或者缺失,它可能会翻译成一个不完整的函数,所以推荐使用完整的
PartialFunction声明,如上面的例子
new PartialFunction[List[Int], Int] {
def apply(xs: List[Int]) = xs match {
case x :: y :: _ => y
}
def isDefinedAt(xs: List[Int]) = xs match {
case x :: y :: _ => true
case _ => false
}
}
只有当isDefinedAt方法返回为true是,apply方法才会去执行,此时我们可以添加额外的逻辑,比如过滤掉某些不符合条件的东东
val list=List(List(1,2),List(1))
val r1=list.collect(second2) //collect有个重载的方法参数为PartialFunction
println(r1) //List(2)
function1和PartialFunction的区别
其实官方的API解释得很明朗,通俗点来说平时我们使用的拉姆达表达式就是FunctionX提供给我们的语法糖,二者的功能完全一样。事实上,PartialFunction是Funtion1的子类,只不过Funtion1仅仅只是一个函数,而PartialFunction可以识别出我们的输入,并进行一些额外的处理
A function of 1 parameter.
In the following example, the definition of succ is a shorthand for the anonymous class definition anonfun1:
object Main extends App {
val succ = (x: Int) => x + 1
val anonfun1 = new Function1[Int, Int] {
def apply(x: Int): Int = x + 1
}
assert(succ(0) == anonfun1(0))
}
Note that the difference between Function1
and scala.PartialFunction is that the latter can specify inputs which it will not handle.
2.部分应用函数
部分应用函数, 是指一个函数有N个参数, 而我们为其提供少于N个参数, 那就得到了一个部分应用函数.
比如我先定义一个函数
def sum(a:Int,b:Int,c:Int) = a + b + c;
那么就可以从这个函数衍生出一个偏函数是这样的:
def p_sum = sum(1, _:Int, _:Int)
于是就可以这样调用p_sum(2,3), 相当于调用sum(1,2,3) 得到的结果是6. 这里的两个_分别对应函数sum对应位置的参数. 所以你也可以定义成
def p_sum = sum (_:Int, 1, _:Int)