为什么这个PartialFunction被定义了,但在Scala中应用后仍然崩溃(正确地)



我想尝试使用深度模式匹配用例的部分函数。最初(当然)在应用Some(Some(3))后,这并不起作用,但似乎被定义为:

def deepTest : PartialFunction [Option[Option[Int]], Int] = {
    case Some(v) => v match {
      case None => 3 
    }
    case None => 1
}

我认为通过解耦嵌套模式匹配,事情会更容易:

def deepTestLvl1 : PartialFunction [Option[Option[Int]], Option[Int]] = {
  case Some(v) => v
  case None => Some(1)
}

def deepTestLvl2 : PartialFunction [Option[Int], Int] = {
  case None => 3
}

但结果如下:

scala> (deepTestLvl1 andThen deepTestLvl2) isDefinedAt(Some(Some(3)))
res24: Boolean = true

以及应用后:

scala> (deepTestLvl1 andThen deepTestLvl2) (Some(Some(3)))
scala.MatchError: Some(3) (of class scala.Some)
    at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:248)
    at scala.PartialFunction$$anon$1.apply(PartialFunction.scala:246)
    at $anonfun$deepTestLvl2$1.applyOrElse(<console>:7)
    at $anonfun$deepTestLvl2$1.applyOrElse(<console>:7)
        ....
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:83)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

我是不是做错了什么?当我按顺序编写deepTestLvl{1,2}并给出正确答案时,isDefinedAt不应该被调用两次吗?

非常好的问题。

让我们检查一下来源,看看背后发生了什么:

override def andThen[C](k: B => C): PartialFunction[A, C] =
  new AndThen[A, B, C] (this, k)

我们可以在这里观察到,andThen甚至不需要分部函数,任何转换结果的函数都可以。您的代码是有效的,因为:trait PartialFunction[-A, +B] extends (A => B)。这实际上可以在文档中找到:

def andThen[C](k: (B) ⇒ C): PartialFunction[A, C]

将此偏函数与应用于此偏函数结果的转换函数组合。

C转换函数的结果类型。

k转换函数

返回与此偏函数具有相同域的偏函数,该函数将参数x映射到k(this(x))

因此,目前没有办法以您想要的方式链接PartialFunction,因为正如Robin所说,这需要应用函数。除了计算成本高昂之外,它还可能产生副作用,这是一个更大的问题。

更新

拼凑出您正在寻找的实现小心使用正如我已经提到的,如果你的代码有副作用,它将导致问题:

implicit class PartialFunctionExtension[-A, B](pf: PartialFunction[A, B]) {
  def andThenPf[C](pf2: PartialFunction[B, C]) = new PfAndThen(pf, pf2)
  class PfAndThen[+C](pf: PartialFunction[A, B], nextPf: PartialFunction[B, C]) extends PartialFunction[A, C] {
    def isDefinedAt(x: A) = pf.isDefinedAt(x) && nextPf.isDefinedAt(pf.apply(x))
    def apply(x: A): C = nextPf(pf(x))
  }
}

尝试一下:

deepTestLvl1.andThenPf(deepTestLvl2).isDefinedAt(Some(Some(3)))  // false
deepTestLvl1.andThenPf(deepTestLvl2).isDefinedAt(Some(None))     // true
deepTestLvl1.andThenPf(deepTestLvl2).apply(Some(None))           // 3

andThen生成的PartialFunction上的isDefinedAt返回不一致结果的原因是它实际上没有将第一个部分函数应用于其参数,这可能是一个昂贵的操作。

这种行为很可能会让人绊倒,而且没有记录在案——你可能需要提交一个补丁来添加文档。

附言:我的猜测是,deepTest之所以如此,是因为部分函数定义的源代码中最外层的match,而只有最外层的匹配,被认为是出于定义的目的——但我认为你必须检查scalac的源代码才能确定。

相关内容

  • 没有找到相关文章

最新更新