已复制
全屏展示
复制代码

Scala偏函数和偏应用函数


· 4 min read

一. 偏函数定义

偏函数是指仅定义了输入参数的子集的函数,下图显示的一个偏函数: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

参考资料

https://blog.csdn.net/qiruiduni/article/details/46914397

🔗

文章推荐