05-scala偏函数

163 阅读1分钟

基础用法

首先我们总结下偏函数的定义:

  1. 只接受一个参数
  2. 输入参数类型范围的子集
  3. 可以显示使用isDefinedAt方法指定输入参数的范围,可以使用apply方法

举个例子(这个例子暂时存在问题,实际情况是,我们基本不用这种定义):

val squareRoot: PartialFunction[Double, Double] = {
  def apply(x: Double) = Math.sqrt(x)
  def isDefinedAt(x: Double) = x >= 0
}

实际使用时,我们更多使用case的方式定义:

package example

object MyExample {
  def main(args: Array[String]): Unit = {
    val evenToPrint: PartialFunction[Int, String] = {
      case x if x % 2 == 0 => s"$x is even number"
    }
    val res = evenToPrint(10)
    println(res)  // 10 is even number
  }
}

如果我们传入奇数,则会抛出运行时异常,比如我们传入1,会有如下的异常:

[error] scala.MatchError: 1 (of class java.lang.Integer)
[error]         at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:369)
[error]         at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:367)
[error]         at example.MyExample$$anonfun$1.applyOrElse(MyExample.scala:5)
[error]         at example.MyExample$$anonfun$1.applyOrElse(MyExample.scala:5)
[error]         at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:35)
[error]         at example.MyExample$.main(MyExample.scala:8)
[error]         at example.MyExample.main(MyExample.scala)
[error]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[error]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[error]         at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[error]         at java.base/java.lang.reflect.Method.invoke(Method.java:566)
[error] stack trace is suppressed; run last Compile / run for the full output
[error] (Compile / run) scala.MatchError: 1 (of class java.lang.Integer)
[error] Total time: 2 s, completed 202244日 上午9:24:09

错误中会有提示具体错误在哪一行,上面很明显在地4-5行提示错误。

偏函数组合

偏函数使用时,一般会有多个组合,并借助orElseandThen这种执行链式组合。

首先看下orElse

package example

object MyExample {
  def main(args: Array[String]): Unit = {
    val evenToString: PartialFunction[Int, String] = {
      case x if x % 2 == 0 => s"$x is even number"
    }
    val oddToString: PartialFunction[Int, String] = {
      case x if x % 2 == 1 => s"$x is odd number"
    }

    val numToString: PartialFunction[Int, String] = {
      evenToString orElse oddToString
    }

    println(numToString(4))
    println(numToString(5))
  }
}

代码输出:

4 is even number
5 is odd number

注意这里补充一点, orElse是一个方法。

orElse可以连接多个偏函数,这里不再赘述。

如果我们想在命中偏函数执行下一个操作,可以借助andThen来实现,我们结合前面的orElse,看下例子:

package example

object MyExample {
  def main(args: Array[String]): Unit = {
    val negativeToPositive: PartialFunction[Int, Int] = {
      case x if x < 0 => -x
    }

    val positiveTimes2: PartialFunction[Int, Int] = {
      case x  if x > 0 => x * 2
    }

    val zeroToOne: PartialFunction[Int, Int] = {
      case 0 => 1
    }
    // 这里串联起来orElse
    val numOp: PartialFunction[Int, Int] = {
      zeroToOne orElse positiveTimes2 orElse negativeToPositive
    }

    val numToPrint: PartialFunction[Int, String] = {
      case x => s"Final number is $x"
    }

    val numOpFinal: PartialFunction[Int, String] = {
      numOp andThen numToPrint  // 这里使用andThen
    }

    println(numOpFinal(0))
    println(numOpFinal(10))
    println(numOpFinal(-5))
  }
}

代码输出:

Final number is 1
Final number is 20
Final number is 5