2014-01-10 72 views
10

讓我們重用日報Scala的例子:鏈接與andThen PartialFunctions在斯卡拉

type PF = PartialFunction[Int,Int] 

val pf1 : PF = {case 1 => 2}      

val pf2 : PF = {case 2 => 3}      

,讓我們補充:

val pf3 : PF = {case 3 => 4} 

andThen工作如下預期:

pf1 andThen pf2 isDefinedAt(x) 

回報true iff x == 1(實際上,pf2不需要是Parti alFunction所有)

不過,我預計:

pf1 andThen pf3 isDefinedAt(x) 

將返回false所有x(即,當且僅當PF1定義,請檢查PF3),但它不只有驗證PF1。

最後,pf1 andThen pf3 lift(x)總是導致MatchError。我寧願得到None ...我可以通過提升每個函數(如pf1.lift(x).flatMap(pf3.lift))來獲得此行爲,但使用純PartialFunction API有沒有更簡單的方法? (且不起重單獨每個部分的功能?)

回答

11

如果你看看andThen

def andThen[C](k: (B) => C): PartialFunction[A, C] 

這構成了接收器功能而不是部分功能。也就是說,k預計將完全定義,它不具有isDefinedAt。因此,由此產生的部分函數不需要改變isDefinedAt的行爲,它仍然只需要查閱第一個部分函數。

你可以編寫自己的擴展,組成兩個部分功能:

implicit class ComposePartial[A, B](pf: PartialFunction[A, B]) { 
    def collect[C](that: PartialFunction[B, C]): PartialFunction[A, C] = 
    new PartialFunction[A, C] { 
     def apply(a: A): C = that(pf(a)) 
     def isDefinedAt(a: A) = pf.isDefinedAt(a) && { 
     val b = pf(a) 
     that.isDefinedAt(b) 
     } 
    } 
} 

pf1 collect pf2 isDefinedAt(1) // true 
pf1 collect pf3 isDefinedAt(1) // false 

的問題是,你必須調用pf(a),所以考慮到Scala沒有強制純度,你可能最終執行的副作用不期望。

+0

我試試這個,但所有'apply'和'isDefinedAt' COLLED兩次。 –

7

對於PartialFunction s,您需要等效於flatMap s。

implicit class CollectPartial[A, B](f: PartialFunction[A, B]) { 
    def collect[C](g: PartialFunction[B, C]) = Function.unlift { a: A => 
     f.lift(a).flatMap(g.lift) 
    } 
} 

使用它像

val a: PartialFunction[String, Int] = ... 
val b: PartialFunction[Int, Char] = ... 
val c: PartialFunction[String, Char] = a collect b 

這工作,因爲即使有副作用的預期。

2

爲什麼不乾脆:

def compose[A,B,C](f: PartialFunction[A, B], g: PartialFunction[B, C]) : PartialFunction[A, C] = 
Function.unlift(f.andThen(g.lift))