天天看點

Scala教程之:PartialFunction

Scala中有一個很有用的traits叫PartialFunction,我看了下别人的翻譯叫做偏函數,但是我覺得部分函數更加确切。

那麼PartialFunction是做什麼用的呢?簡單點說PartialFunction用在模式比對中,是一個不完整的函數,它隻實作了函數的部分功能,也就是列舉了部分case的情況。

我們先看下PartialFunction的定義:

trait PartialFunction[-A, +B] extends (A => B) { 
    ...

 def isDefinedAt(x: A): Boolean

    ...
           

我們可以看到PartialFunction是一個trait,它繼承自函數 (A => B), 這個函數有一個參數和一個傳回值,在Scala中,該函數會被自動解析為Function1。

我們看下Function1的定義:

trait Function1[@specialized(scala.Int, scala.Long, scala.Float, scala.Double) -T1, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double) +R] extends AnyRef { self =>
  /** Apply the body of this function to the argument.
   *  @return   the result of function application.
   */
  def apply(v1: T1): R
           

我們可以看到Function1定義了一個方法: def apply(v1: T1): R

PartialFunction也定義了一個方法: def isDefinedAt(x: A): Boolean

如果我們要自己實作一個PartialFunction,則必須實作上述兩個方法:

val inc = new PartialFunction[Any, Int] {
    override def isDefinedAt(x: Any): Boolean = ???

    override def apply(v1: Any): Int = ???
  }
           

其中isDefinedAt用來選擇PartialFunction入參的範圍,而apply是真正的業務邏輯。

除了用new來執行個體化一個PartialFunction外,還有一個最簡單的方法就是使用case語句。 我們舉個例子, 如果我們有段case邏輯是比對各個好吃等級,如下:

println("Step 1: Review of Pattern Matching in Scala")
  val donut = "Glazed Donut"
  val tasteLevel = donut match {
    case "Glazed Donut" | "Strawberry Donut" => "Very tasty"
    case "Plain Donut" => "Tasty"
    case _  => "Tasty"
  }
  println(s"Taste level of $donut = $tasteLevel")
           

我們使用了3個case語句,看起來比較繁瑣,使用PartialFunction, 我們可以将其轉換為如下的形式:

val donutTaste = isVeryTasty orElse isTasty orElse unknownTaste
  println(donutTaste("Glazed Donut"))
  println(donutTaste("Plain Donut"))
  println(donutTaste("Chocolate Donut"))
           

PartialFunction可以通過使用orElse關鍵字來合并成一個完整的Function。

我們看下這幾個PartialFunction該怎麼定義:

val isVeryTasty: PartialFunction[String, String] = {
    case "Glazed Donut" | "Strawberry Donut" => "Very Tasty"
  }

  val isTasty: PartialFunction[String, String] = {
    case "Plain Donut" => "Tasty"
  }

  val unknownTaste: PartialFunction[String, String] = {
    case donut1 @ _ => s"Unknown taste for donut = $donut1"
  }

           

實際上就是把整個的業務邏輯,用PartialFunction拆分開來了。這裡使用case語句,會自動轉換成為PartialFunction。

關注下最後一個unknownTaste的case語句, @ 使用來做模式比對的, case donut1 @ _ 就意味着 donut1 将會比對所有的輸入。

更多教程請參考 flydean的部落格