简体   繁体   English

在Scala中使用andThen链接PartialFunctions

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM