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的部落格