scala函數式編程 之偏函數(partial function)的簡單使用

什麼是偏函數(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”) ,請完成如下要求:

  1. 將集合 list 中的所有數字+1,並返回一個新的集合
  2. 要求忽略掉 非數字 的元素,即返回的 新的集合 形式爲 (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]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章