[英]Chaining PartialFunctions with andThen in Scala
Let us reuse examples from Daily scala : 让我们重用Daily scala中的示例:
type PF = PartialFunction[Int,Int]
val pf1 : PF = {case 1 => 2}
val pf2 : PF = {case 2 => 3}
and let us add: 让我们补充一下:
val pf3 : PF = {case 3 => 4}
andThen works as expected here: 然后按预期工作:
pf1 andThen pf2 isDefinedAt(x)
returns true
iff x == 1
(actually, pf2
does not need to be a PartialFunction at all) 返回
true
iff x == 1
(实际上, pf2
根本不需要是PartialFunction)
However, I expected that: 但是,我预计:
pf1 andThen pf3 isDefinedAt(x)
would return false
for all x
(ie, iff pf1 is defined, check for pf3), but it does not and only validates pf1. 将为所有
x
返回false
(即,iff pf1已定义,检查pf3),但它不会且仅验证pf1。
In the end, pf1 andThen pf3 lift(x)
always result in a MatchError. 最后,
pf1 andThen pf3 lift(x)
总是导致MatchError。 I would prefer to get None… I can obtain this behavior by lifting each function such as in pf1.lift(x).flatMap(pf3.lift)
but is there any easier way using pure PartialFunction API? 我宁愿得到None ...我可以通过提升每个函数来获得这种行为,例如在
pf1.lift(x).flatMap(pf3.lift)
但使用纯PartialFunction API有没有更简单的方法? (and without lifting each partial function individually?) (并且不单独提升每个部分功能?)
If you look at andThen
: 如果你看看
andThen
:
def andThen[C](k: (B) => C): PartialFunction[A, C]
This composes the receiver with a function and not a partial function . 这使接收器具有功能而不是部分功能 。 That is,
k
is expected to be fully defined, it doesn't have isDefinedAt
. 也就是说,
k
预计是完全定义的,它没有isDefinedAt
。 Therefore, the resulting partial function does not need to alter the behaviour of isDefinedAt
, it will still just has to consult the first partial function. 因此,由此产生的部分函数不需要改变
isDefinedAt
的行为,它仍然只需要参考第一个部分函数。
You could write your own extension that composes two partial functions: 您可以编写自己的扩展,组成两个部分函数:
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
The problem is that you have to invoke pf(a)
, so given that Scala doesn't enforce purity, you may end up executing side effects unwantedly. 问题是你必须调用
pf(a)
,所以鉴于Scala没有强制执行纯度,你最终可能会不必要地执行副作用。
You need the equivalent of flatMap
for PartialFunction
s. PartialFunction
需要等效的flatMap
。
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)
}
}
Use it like 像它一样使用它
val a: PartialFunction[String, Int] = ...
val b: PartialFunction[Int, Char] = ...
val c: PartialFunction[String, Char] = a collect b
This works as expected even with side-effects. 即使有副作用,这也可以按预期工作。
Why not simply : 为什么不简单:
def compose[A,B,C](f: PartialFunction[A, B], g: PartialFunction[B, C]) : PartialFunction[A, C] =
Function.unlift(f.andThen(g.lift))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.