让我们重用Daily scala中的示例:
type PF = PartialFunction[Int,Int]
val pf1 : PF = {case 1 => 2}
val pf2 : PF = {case 2 => 3}
让我们添加:
val pf3 : PF = {case 3 => 4}
然后按预期工作:
pf1 andThen pf2 isDefinedAt(x)
返回true
iff x == 1
(实际上,pf2
根本不需要是PartialFunction)
然而,我期待:
pf1 andThen pf3 isDefinedAt(x)
将为所有false
返回x
(即,iff pf1已定义,请检查pf3),但它不会且仅验证pf1。
最后,pf1 andThen pf3 lift(x)
总是会导致MatchError。我宁愿得到None ......我可以通过提升pf1.lift(x).flatMap(pf3.lift)
中的每个函数来获得这种行为,但使用纯PartialFunction API有没有更简单的方法? (并且不单独提升每个部分功能?)
答案 0 :(得分: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不会强制执行纯度,您可能会不必要地执行副作用。
答案 1 :(得分:7)
对于flatMap
s,您需要等效PartialFunction
。
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 :(得分:2)
为什么不简单:
def compose[A,B,C](f: PartialFunction[A, B], g: PartialFunction[B, C]) : PartialFunction[A, C] =
Function.unlift(f.andThen(g.lift))