11

让我们重用 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)

返回trueiff x == 1(实际上,pf2根本不需要是 PartialFunction)

但是,我预计:

pf1 andThen pf3 isDefinedAt(x)

将返回false所有x(即,如果定义了 pf1,则检查 pf3),但它不会且仅验证 pf1。

最后,pf1 andThen pf3 lift(x)总是会导致 MatchError。我宁愿得到 None... 我可以通过提升每个函数来获得这种行为,例如 inpf1.lift(x).flatMap(pf3.lift)但是使用纯 PartialFunction API 有没有更简单的方法?(并且没有单独提升每个部分功能?)

4

3 回答 3

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 不强制执行纯度,您最终可能会意外执行副作用。

于 2014-01-10T10:27:47.030 回答
7

您需要相当于flatMapfor PartialFunctions。

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

即使有副作用,这也能按预期工作。

于 2014-11-22T18:00:48.360 回答
2

为什么不简单地:

def compose[A,B,C](f: PartialFunction[A, B], g: PartialFunction[B, C]) : PartialFunction[A, C] =
Function.unlift(f.andThen(g.lift))
于 2015-03-11T16:01:00.987 回答