国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

一文讀懂Scala中的偏函數

開發 開發工具
藝術地說,Scala中的Partial Function就是一個“殘缺”的函數,Partial Function做不到以“偏”概全,因而需要將多個偏函數組合,最終才能達到全面覆蓋的目的。

藝術地說,Scala中的Partial Function就是一個“殘缺”的函數,就像一個嚴重偏科的學生,只對某些科目感興趣,而對沒有興趣的內容棄若蔽履。Partial Function做不到以“偏”概全,因而需要將多個偏函數組合,最終才能達到全面覆蓋的目的。所以這個Partial Function確實是一個“部分”的函數。

一文讀懂Scala中的偏函數

對比Function和Partial Function,更學術味的解釋如下:

  • 對給定的輸入參數類型,函數可接受該類型的任何值。換句話說,一個(Int) => String 的函數可以接收任意Int值,并返回一個字符串。
  • 對給定的輸入參數類型,偏函數只能接受該類型的某些特定的值。一個定義為(Int) => String 的偏函數可能不能接受所有Int值為輸入。

在Scala中,所有偏函數的類型皆被定義為PartialFunction[-A, +B]類型,PartialFunction[-A, +B]又派生自Function1。由于它僅僅處理輸入參數的部分分支,因而它通過isDefineAt()來判斷輸入值是否應該由當前偏函數進行處理。PartialFunction的定義如下所示:

  1. trait PartialFunction[-A, +B] extends (A => B) { self => 
  2.   import PartialFunction._ 
  3.   def isDefinedAt(x: A): Boolean 
  4.   def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = 
  5.     if (isDefinedAt(x)) apply(x) else default(x) 

既然偏函數僅處理部分分支,自然可以與模式匹配結合起來。case語句從本質上講就是PartialFunction的子類。當我們定義了如下值:

  1. val p:PartialFunction[Int, String] = { case 1 => "One" } 

實際上就是創建了一個PartialFunction[Int, String]的子類,其中isDefineAt方法提供類似這樣的實現:

  1. def isDefineAt(x: Int):Boolean = x == 1 

當我們通過p(1)去調用該偏函數時,就相當于調用了Int => String函數的apply()方法,從而返回轉換后的值“one”。如果傳入的參數使得isDifineAt返回false,就會拋出MatchError異常。追本溯源,是因為這里對偏函數值的調用,實則是調用了AbstractPartialFunction的apply()方法(case語句相當于是繼承AbstractPartialFunction的子類):

  1. abstract class AbstractPartialFunction[ 
  2. @specialized(scala.Int, scala.Long, scala.Float, scala.Double, scala.AnyRef) -T1,  
  3. @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double, scala.AnyRef) +R]  
  4. extends Function1[T1, R] with PartialFunction[T1, R] { self => 
  5.     def apply(x: T1): R = applyOrElse(x, PartialFunction.empty) 

apply()方法內部調用了PartialFunction的applyOrElse()方法。若isDefineAt(x)返回為false,就會將x值傳遞給PartialFunction.empty。這個empty等于類型為PartialFunction[Any, Nothong]的值empty_pf,定義如下:

  1. private[this] val empty_pf: PartialFunction[Any, Nothing] = new PartialFunction[Any, Nothing] { 
  2.    def isDefinedAt(x: Any) = false 
  3.    def apply(x: Any) = throw new MatchError(x) 
  4.    override def orElse[A1, B1](that: PartialFunction[A1, B1]) = that 
  5.    override def andThen[C](k: Nothing => C) = this 
  6.    override val lift = (x: Any) => None 
  7.    override def runWith[U](action: Nothing => U) = constFalse 
  8.  } 

這正是執行p(2)會拋出MatchError的由來。

為什么要用偏函數呢?以我個人愚見,還是一個重用粒度的問題。函數式的編程思想是以一種“演繹法”而非“歸納法”去尋求解決空間。也就是說,它并不是要去歸納問題然后分解問題并解決問題,而是看透問題本質,定義最原初的操作和組合規則,面對問題時,可以通過組合各種函數去解決問題,這也正是“組合子(combinator)”的含義。偏函數則更進一步,將函數求解空間中各個分支也分離出來,形成可以被組合的偏函數。

偏函數中最常見的組合方法為orElse、andThen與compose。orElse相當于一個或運算,如果通過它將多個偏函數組合起來,就相當于形成了多個case合成的模式匹配。倘若所有偏函數滿足了輸入值的所有分支,組合起來就形成一個函數了。例如寫一個求絕對值的運算,就可以利用偏函數:

  1. val positiveNumber:PartialFunction[Int, Int] = {  
  2.     case x if x > 0 => x  
  3. val zero:PartialFunction[Int, Int] = {  
  4.     case x if x == 0 => 0  
  5. val negativeNumber:PartialFunction[Int, Int] = {  
  6.     case x if x < 00 => -x  
  7. def abs(x: Int): Int = { 
  8.     (positiveNumber orElse zero orElse negativeNumber)(x) 

利用orElse組合時,還可以直接組合case語句,例如:

  1. val pf: PartialFunction[Int, String] = { 
  2.   case i if i%2 == 0 => "even" 
  3. val tf: (Int => String) = pf orElse { case _ => "odd" } 

orElse被定義在PartialFunction類型中,而andThen與compose卻不同,它們實則被定義在Function中,PartialFunction只是重寫了這兩個方法。這意味著函數之間的組合可以使用andThen與compose,偏函數也可以。這兩個方法的功能都是將多個(偏)函數組合起來形成一個新函數,只是組合的順序不同,andThen是組合***個,接著是第二個,依次類推;而compose則順序相反。

利用andThen組合偏函數,設計本質接近Pipe-and-Filter模式,每個偏函數都可以理解為是一個Filter。因為要將這些偏函數組合起來形成一個管道,這就要求被組合的偏函數其輸入值與輸出值必須支持可串接,即上一個偏函數的輸出值會作為下一個偏函數的輸入值。對比orElse,則有所不同,orElse要求組合的所有偏函數必須是同樣類型的偏函數定義,例如都是Int => String,或者String => CustomizedClass。

在PartialFunction中,andThen方法返回的是一個名為AndThen的偏函數:

  1. trait PartialFunction[-A, +B] extends (A => B) { 
  2.   override def andThen[C](k: B => C): PartialFunction[A, C] = 
  3.     new AndThen[A, B, C] (this, k) 
  4. object PartialFunction { 
  5.   private class AndThen[-A, B, +C] (pf: PartialFunction[A, B], k: B => C) extends PartialFunction[A, C] { 
  6.     def isDefinedAt(x: A) = pf.isDefinedAt(x) 
  7.     def apply(x: A): C = k(pf(x)) 
  8.     override def applyOrElse[A1 <: A, C1 >: C](x: A1, default: A1 => C1): C1 = { 
  9.       val z = pf.applyOrElse(x, checkFallback[B]) 
  10.       if (!fallbackOccurred(z)) k(z) else default(x) 
  11.     } 
  12.   } 

注意看,andThen接收的參數為k: B => C,即函數類型而非偏函數類型。當然,由于偏函數繼承自函數,它也可以組合偏函數。如果andThen組合了偏函數,則要求輸入參數必須滿足所有參與組合的偏函數,否則就會拋出MatchError錯誤。例如編寫一個函數,要求將字符串中的數字替換為對應的英文單詞,則可以實現為:

  1. val p1:PartialFunction[String, String] = {  
  2.     case s if s.contains("1") => s.replace("1", "one")  
  3. val p2:PartialFunction[String, String] = {  
  4.     case s if s.contains("2") => s.replace("2", "two")  
  5. val p = p1 andThen p2 

如果調用p("123"),返回結果為"onetwo3",但如果傳入p("13"),由于p2偏函數的isDefineAt返回false,就會拋出MatchError錯誤。

偏函數可以用在很多場景。例如我們可以利用orElse之類的語義,編寫DSL風格的代碼,使其更加靈活且可讀。《DSL in Action》一書中就是用了orElse來處理金融行業的需求:

  1. val forHKG:PartialFunction[Market, List[TaxFee]] = ... 
  2. val forSGP:PartialFunction[Market, List[TaxFee]] = ... 
  3. val forAll:PartialFunction[Market, List[TaxFee]] = ... 
  4.  
  5. def forTrade(trade: Trade): List[TaxFee] =  
  6.     (forHKG orElse forSGP orElse forAll)(trade.market) 

也可以有效地利用偏函數的開放性,使得API的調用者可以根據具體的需求場景傳入自己的case語句。例如Twitter的Effective Scala給出的案例:

  1. trait Publisher[T] { 
  2.   def subscribe(f: PartialFunction[T, Unit]) 
  3. val publisher: Publisher[Int] = ... 
  4.  
  5. publisher.subscribe { 
  6.   case i if isPrime(i) => println("found prime", i) 
  7.   case i if i%2 == 0 => count += 2 
  8.   /* ignore the rest */ 

定義在AKKA的Actor中的receive()方法也是一個偏函數:

  1. trait Actor { 
  2.     type Receive = Actor.Receive 
  3.     def receive: Actor.Receive 
  4. object Actor { 
  5.   type Receive = PartialFunction[Any, Unit] 

由于偏函數繼承自函數,因而,如果一個方法要求接收函數,那么它也可以接收偏函數。例如我們常常使用的map、filter等方法,就可以接收偏函數:

  1. val sample = 1 to 10 
  2. sample map { 
  3.     case x if x % 2 == 0 => x + " is even" 
  4.     case x if x % 2 == 1 => x + " is odd" 

在Twitter的Effetive Scala中,給出了一個使用map的編碼風格建議:

  1. //avoid 
  2. list map { item => 
  3.   item match { 
  4.     case Some(x) => x 
  5.     case None => default 
  6.   } 
  7. //recommend 
  8. list map { 
  9.   case Some(x) => x 
  10.   case None => default 

從本質上講,假設這個list的類型為List[Option[String]],則前者傳給map的其實是一個形如Option[String] => String的函數,后者則通過case語句創建了PartialFunction[Option[String], String]的實例傳遞給了map。

【本文為51CTO專欄作者“張逸”原創稿件,轉載請聯系原作者】

戳這里,看該作者更多好文

責任編輯:趙寧寧 來源: 51CTO專欄
相關推薦

2009-07-22 07:42:00

Scala偏應用函數

2023-12-22 19:59:15

2021-08-04 16:06:45

DataOps智領云

2022-04-20 11:10:17

bias推薦系統debias

2021-09-04 19:04:14

配置LogbackJava

2022-11-06 21:14:02

數據驅動架構數據

2018-09-28 14:06:25

前端緩存后端

2025-04-03 10:56:47

2022-09-22 09:00:46

CSS單位

2019-12-17 08:16:04

JavaScriptthis編程

2023-11-27 17:35:48

ComponentWeb外層

2023-05-20 17:58:31

低代碼軟件

2022-12-01 17:23:45

2022-10-20 08:01:23

2022-07-05 06:30:54

云網絡網絡云原生

2025-10-14 09:01:20

2021-12-29 18:00:19

無損網絡網絡通信網絡

2022-07-26 00:00:03

語言模型人工智能

2018-10-18 11:00:50

人工智能機器學習模型偏差

2023-11-21 09:41:00

緩存策略存儲
點贊
收藏

51CTO技術棧公眾號

综合久久综合久久| 粉嫩久久99精品久久久久久夜| 超清福利视频| 美女av一区二区| 欧美aa国产视频| 国产丝袜视频在线播放| 日本wwww视频| 99re在线视频观看| 亚洲精品日韩丝袜精品| 亚洲老头同性xxxxx| 国产精品全国免费观看高清| 综合久久久久久| 亚洲五月六月丁香激情| 视频一区国产视频| 久久激情av| 毛片免费在线| 精品日本一区二区三区| 亚洲男人天堂手机在线| 中文字幕国产精品一区二区| 日韩在线欧美| 六月婷婷一区| 成人av免费在线观看| 欧美成免费一区二区视频| 欧美xxxxhdvideosex| 第一福利在线视频| 性欧美孕妇孕交| 中文视频一区视频二区视频三区| 一本一本久久a久久精品综合小说 一本一本久久a久久精品牛牛影视 | 先锋影音资源综合在线播放av| 成人一区二区三区四区| 久久狠狠久久综合桃花| 日韩中文字幕精品| 国产精品久久久久久网站 | 国产91富婆露脸刺激对白| 日韩av自拍| 久久av影院| 97电影在线| 日本fc2在线观看| 日韩黄色片视频| 在线观看视频你懂的| 日韩成人av网站| 日日噜噜噜噜久久久精品毛片| 亚洲国产欧美日韩| 国产精品电影网站| 日韩亚洲欧美一区二区| 蜜桃视频在线观看91| 国产精品高潮粉嫩av| 欧美三日本三级少妇三99| 成人动漫网站在线观看| 日韩有码在线视频| 日韩久久午夜影院| 91精品国产综合久久久久久久久| 国模私拍一区二区三区| 最近2019免费中文字幕视频三 | 欧美日韩免费观看一区三区| 尤物yw午夜国产精品视频| 欧美日韩国产欧美日美国产精品| 亚洲欧洲日产国产综合网| 精品国产乱码久久久久久闺蜜| 在线日韩av片| 亚洲一二三区在线观看| 中文字幕亚洲自拍| 亚洲精品在线不卡| 污色网站在线观看| 可播放的18gay1069| 噜噜噜天天躁狠狠躁夜夜精品| www在线观看播放免费视频日本| 爆乳熟妇一区二区三区霸乳| 日本丰满大乳奶| 亚洲乱码一区二区三区三上悠亚 | 日韩一区二区三区电影| 中文字幕在线播放不卡一区| 亚洲少妇中出一区| 欧美韩国日本一区| 日韩国产欧美区| 国产亚洲视频在线观看| 久久精品99久久| 亚洲欧洲闷骚av少妇影院| 亚洲精品网站在线观看| 国产成人精品午夜| 国产乱人伦真实精品视频| h动漫在线视频| 91丨porny丨户外露出| 亚洲男女一区二区三区| 国产91视频一区| 182在线视频观看| 91麻豆高清视频| 欧美激情精品久久久久久| 国产乱子夫妻xx黑人xyx真爽| 蜜臀a∨国产成人精品| 欧美va亚洲va在线观看蝴蝶网| 国产精品高潮视频| 日韩欧美在线电影| 午夜精品久久久久99热蜜桃导演| 欧美影视一区在线| 99久热re在线精品视频| 三年中国中文在线观看免费播放| 久久亚裔精品欧美| 不卡一区二区三区视频| 奇米777欧美一区二区| 久久夜色精品国产噜噜av小说| 人成免费电影一二三区在线观看| 国产成人精品1024| 国产成人在线视频网址| 91一区二区三区在线观看| 日韩精品欧美激情| 午夜精品久久久久久99热| 性欧美长视频免费观看不卡| 亚洲综合中文字幕68页| 中文字幕一区二区在线播放| 嗯啊主人调教在线播放视频 | 国产视频福利在线| 亚洲视频www| 高清国产一区二区| 一区2区3区在线看| 国产精选久久久久久| 日韩另类在线| 天美av一区二区三区久久| 亚洲成人av一区二区三区| 婷婷开心激情综合| 懂色av一区二区三区在线播放| 九七电影韩国女主播在线观看| 97精品久久久午夜一区二区三区 | 麻豆导航在线观看| 影音先锋中文字幕一区| 亚洲高清久久网| 日本韩国在线不卡| 亚洲图片欧洲图片日韩av| 国产精品99久久免费黑人人妻| 亚洲动漫精品| 激情综合色综合久久综合| 亚洲国产精品一区二区尤物区| 不卡一区二区三区视频| 国产精品天堂蜜av在线播放 | 欧美狂野激情性xxxx在线观| 91.·福利| 中文字幕国产在线| 男人天堂视频在线观看| 久久综合久久鬼色| 欧美日韩国产精选| 91精品国产色综合久久不卡98口 | 亚洲成人久久久久| 日韩精品一区二区在线视频 | 成人在线视频网| 伊人久久在线观看| 日本在线视频网| 高清在线不卡av| 日韩一级精品视频在线观看| 午夜精品美女自拍福到在线| 亚洲成人网上| 永久免费网站在线| 99麻豆久久久国产精品免费优播| 一本大道久久精品懂色aⅴ| 97国产suv精品一区二区62| 男同在线观看| 69xxxx欧美| www日韩大片| 亚洲www在线观看| 亚洲视频一起| 亚洲第一精品福利| 老司机aⅴ毛片免费观看| 99亚洲视频| 精品88久久久久88久久久| 国内精品久久影院| 亚洲色图15p| 欧美高清在线视频观看不卡| 欧美一区二区麻豆红桃视频| 成人高清免费观看| 999热视频| 伊人婷婷久久| 红杏一区二区三区| 91在线精品秘密一区二区| 欧美性大战久久久| 精品一区日韩成人| 成人网视频在线观看| 99久久精品免费精品国产| 4438x成人网最大色成网站| 麻豆电影在线观看| 1000精品久久久久久久久| 国产一区二区三区免费视频| 日韩妆和欧美的一区二区| 日本在线免费中文字幕| 中文字幕一区二区三中文字幕| 久久中文字幕一区| 亚洲77777| 91最新地址在线播放| 日韩专区在线播放| 精品少妇在线视频| 亚洲一区二区三区在线免费| 日韩欧美成人一区| 久久亚洲午夜电影| 亚洲免费看片| 欧美精品一区二区三区蜜桃| 超碰免费97在线观看| 91久久精品一区二区| 国产av熟女一区二区三区| 欧美日韩精品一区视频| a级黄色一级片| 猫咪成人在线观看| 久久久久久成人|