Scala偏函数和偏应用函数
一. 偏函数定义
偏函数是指仅定义了输入参数的子集的函数,下图显示的一个偏函数:f : X -> Y,该函数仅定义了输入参数X的子集1和3,没有包含2。
Scala中的 PartialFunction 是一个Trait(trait PartialFunction[-A, +B] extends (A) => B),其中接收一个类型为A的参数,返回一个类型为B的结果。
偏函数特点:
- 1)是一个将类型A转为类型B的特质。
- 2)接受A类型的输入参数,返回值为B类型。
- 3)是一个一元函数
- 4)-A表明为A类型或A类型的父类,即输入的参数应为A的子集,具有“部分”的含义。
- 5)+B表明可以是B或B的子类,具有“全部”的含义。
二. 偏函数使用场景
对某些值现在还无法给出具体的操作(即需求还不明朗),也有可能存在几种处理方式(具体的需求),我们可以先对需求明确的部分进行定义,然后视具体情况补充其他域的定义。
比如下面定义了大于1和小于01的所有整数域(大于1返回1,小于-1返回),后期可以使用orElse等来定义其他的域,后面有示例。
val signal: PartialFunction[Int, Int] = {
case x if x > 1 => 1
case x if x < -1 => -1
}
// signal(10) 返回 1
// signal(-5) 返回 -1
三. 偏函数内部方法
偏函数有一些内部方法:apply、applyOrElse、isDefinedAt、OrElse、compose、 andThen
apply
函数调用
val signal: PartialFunction[Int, Int] = {
case x if x > 1 => 1
case x if x < -1 => -1
case 0 => 0
}
signal.apply(2)
// 等价于
signal(2)
applyOrElse
它接收2个参数,第一个是调用的参数,第二个是个回调函数。如果第一个调用的参数匹配,返回匹配的值,否则调用回调函数。
val signal: PartialFunction[Int, Int] = {
case x if x > 1 => 1
case x if x < -1 => -1
case 0 => 0
}
val value: Any = signal.applyOrElse(1, { x: Int => "call me" })
println(value)
// 返回 call me
// 由于 1 不匹配,所以调用的回调函数
isDefinedAt
signal所引用的函数对0值没有定义,调用signal(0) 会抛出异常,因此使用前最好先 signal.isDefinedAt(0) 判断一下。
val signal: PartialFunction[Int, Int] = {
case x if x > 1 => 1
case x if x < -1 => -1
}
signal.isDefinedAt(0)
// 返回 false
orElse
添加其他定义域
val signal: PartialFunction[Int, Int] = {
case x if x > 1 => 1
case x if x < -1 => -1
}
val newSignal: PartialFunction[Int,Int] = signal.orElse{
case 0 => 0
}
newSignal(0)
// 返回 0
compose
对定义域做一些修改
val signal: PartialFunction[Int, Int] = {
case x if x > 1 => 1
case x if x < -1 => -1
case 0 => 0
}
val newSignal: Function1[Int, Int] = signal.compose((x: Int) => x - 1)
println(newSignal(5)) // 返回 1 相当于 signal(f1(5)), f1 = (x: Int) => x - 1
println(newSignal(1)) // 返回 0 相当于 signal(f1(1)), f1 = (x: Int) => x - 1
println(newSignal(-5)) // 返回-1 相当于 signal(f1(-5)),f1 = (x: Int) => x - 1
andThen
前一个方法的调用结果作为后一个方法的输入参数。
val signal: PartialFunction[Int, Int] = {
case x if x > 1 => 1
case x if x < -1 => -1
case 0 => 0
}
val anotherSignal: PartialFunction[Int, Int] = {
case 0 => 0
case x if x > 0 => x - 1
case x if x < 0 => x + 1
}
val newSignal: PartialFunction[Int, Int] = anotherSignal andThen signal
println(newSignal(10)) // 1 = signal(newSignal(10))
println(newSignal(1)) // 0 = signal(newSignal(1))
println(newSignal(-1)) // 0 = signal(newSignal(-1))
println(newSignal(0)) // 0 = signal(newSignal(0))
四. 偏应用函数
偏应用函数类似于柯里化,是指一个函数有n个参数,我们为其提供一些固定的参数数据,最终可以输入少于n个参数,那就得到了一个偏应用函数。
- 柯里化示例
def add(x: Int)(y: Int)(z: Int) = x + y + z
val add1 = add(4)_ // 固定了x为4
val add2 = add1(5) // 固定了x为4, y为5
println(add(4)(5)(6)) // 返回 15
println(add1(5)(6)) // 返回 15
println(add2(6)) // 返回 15
- 手动实现固定参数
def add(x: Int, y: Int, z: Int) = x + y + z
def add1 = add(4, _: Int, 6) // 固定 y 为6
println(add1(5)) // 返回15
参考资料