2014-01-10 45 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))