kun-song / Functional-Programming-in-Scala-Specialization

https://www.coursera.org/specializations/scala
0 stars 0 forks source link

偏函数 #2

Open kun-song opened 6 years ago

kun-song commented 6 years ago

使用 case 表达式可以实现偏函数:

val f: String => String = {
  case "Ping" ⇒ "Pong"
}

f("Ping")
f("Pong")  // 抛出异常

f 函数被编译器展开如下:

new Function1[String, String] {
  def apply(x: String) = x match {
    case "Ping" => "Pong"
  }
}

可以看到,如果使用 Pong 调用 f 会抛出异常,因为 f 定义域并非全部 String,可以将其类型修改为偏函数:

val g: PartialFunction[String, String] = {
  case "Ping" ⇒ "Pong"
}

g.isDefinedAt("Ping")
g.isDefinedAt("Pong")

偏函数可以使用 isDefinedAt 判断参数是否在偏函数定义域上,从而避免抛出异常,而 PartialFunction 定义如下:

trait PartialFunction[-A, +B] extends (A => B) {
  def isDefinedAt(x: A): Boolean
}

g 函数被编译器展开如下:

new PartialFunction[String, String] {
  def apply(x: String) = x match {
    case "ping" => "pong"
  }
  def isDefinedAt(x: String) = x match {
    case "ping" => true
    case _ => false
  }
}

但注意,isDefinedAt 返回 true 并不意味着万事大吉,例如:

val g: PartialFunction[List[Int], String] = {
  case Nil => "one"
  case x :: rest =>
    rest match {
      case Nil => "two"
    }
}

上面例子中,g.isDefinedAt(List(1, 2, 3)) 返回 true,但是实现调用时,函数将抛出异常,该例说明 PartialFunction 只能匹配最外层的 case 表达式,内层无法顾忌,从而内层可能因为各种各样的原因抛出异常。