文章目錄
什麼是偏函數(partial function)
官方文檔的定義:
所謂偏函數(也叫部分函數)與完全函數想對應,普通的方法都是完全函數,即 f(i:Int) = xxx 是將所有Int類型作爲參數的,是對整個Int集的映射;而偏函數則是對部分數據的映射,是一種不爲每個可能的輸入值都提供答案的函數。 它僅提供自己接受數據的答案,並定義了它可以處理的數據。 在Scala中,還可以查詢部分函數以確定它是否可以處理特定值。
爲什麼要使用偏函數有什麼好處
1 在完全函數中我們定義一個除法的函數。
val divide = (x: Int) => 42 / x
2 當我們輸入 0 的時候將會出現問題
scala> divide(0)
java.lang.ArithmeticException: / by zero
這時候我們通常使用一些異常的處理來解決這個問題
Scala允許將除法功能定義爲PartialFunction
。 這樣做時,還明確聲明在輸入參數不爲零時定義了該函數:
val divide = new PartialFunction[Int, Int] {
def apply(x: Int) = 42 / x
def isDefinedAt(x: Int) = x != 0
}
有了 偏函數可以做一些使用前的測試
scala> divide.isDefinedAt(1)
res0: Boolean = true
scala> if (divide.isDefinedAt(1)) divide(1)
res1: AnyVal = 42
scala> divide.isDefinedAt(0)
res2: Boolean = false
在使用偏函數的時候不一定要按照最上面的那樣去使用它,也可以寫一個簡單的用法來實現相同的功能。
val divide2: PartialFunction[Int, Int] = {
case d: Int if d != 0 => 42 / d
}
同樣可以實現上面的功能
scala> divide2.isDefinedAt(0)
res0: Boolean = false
scala> divide2.isDefinedAt(1)
res1: Boolean = true
使用偏函數的好處:
1 可以實現將過濾操作在一個函數裏實現。
看韓順平老師的scala語言核心編程的時候有這樣一個例子
給你一個集合 val list = List(1, 2, 3, 4, “abc”) ,請完成如下要求:
- 將集合 list 中的所有數字+1,並返回一個新的集合
- 要求忽略掉 非數字 的元素,即返回的 新的集合 形式爲 (2, 3, 4, 5)
解決方式一 模式匹配
/**
* 模式匹配
* */
def addOne2( i :Any ):Any = {
i match {
case x:Int => x + 1
case _ =>
}
}
解決方式二 map 遍歷 + filter 過濾
def caseDemo ={
//定義要遍歷的列表
val list = List(1,2,3,4,"hello")
//先過濾然後 再去map
println(list.filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int]).map(i => i+1))
}
偏函數
val list = List(1, 2, 3, 4, "hello")
//定義一個偏函數
//1. PartialFunction[Any,Int] 表示偏函數接收的參數類型是 Any,返回類型是 Int
//2. isDefinedAt(x: Any) 如果返回 true ,就會去調用 apply 構建對象實例,如果是 false,過濾
//3. apply 構造器 ,對傳入的值 + 1,並返回(新的集合)
val partialFun = new PartialFunction[Any,Int] {
override def isDefinedAt(x: Any) = { println("x=" + x)
x.isInstanceOf[Int]
}
override def apply(v1: Any) = {
println("v1=" + v1)
v1.asInstanceOf[Int] + 1}
}
//使用偏函數
//說明:如果是使用偏函數,則不能使用 map,應該使用 collect
//說明一下偏函數的執行流程
//1. 遍歷 list 所有元素
//2. 然後調用 val element = if(partialFun-isDefinedAt(list 單個元素)) {partialFun-apply(list 單個元素) }
//3. 每得到一個 element,放入到新的集合,最後返回
val list2 = list.collect(partialFun)
println("list2" + list2)
偏函數的實現原理
PartialFunction是一個trait
trait PartialFunction[-A, +B] extends (A) => B
可以將=>符號視爲一個轉換器,在這種情況下,可以將(A)=> B解釋爲將類型A轉換爲結果類型B的函數。
Scala中的map與collect
在看scala 偏函數的時候其中的案例需要用到collect方法。不僅是要在工具箱中使用其他工具,還因爲它們已在某些庫(包括Scala集合庫)的API中使用。
List(1, 3, 5, "seven") map { case i: Int => i + 1 } //won't work
//scala.MatchError: seven (of class java.lang.String)
List(1, 3, 5, "seven") collect { case i: Int => i + 1 } //it works
scala 中的map 和 collect 的區別
def map[B](f: (A) ⇒ B): List[B]
def collect[B](pf: PartialFunction[A, B]): List[B]