3

我想尝试使用深度模式匹配用例的部分函数。这最初(当然)在应用 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 两次吗?

4

2 回答 2

3

非常好的问题。

让我们检查一下源代码,看看幕后发生了什么:

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

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

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

将此部分函数与应用于此部分函数结果的转换函数组合在一起。

C转换函数的结果类型。

k变换函数

返回与此部分函数具有相同域的部分函数,​​它将参数映射xk(this(x)).

所以目前没有办法以PartialFunction你想要的方式链接 s,因为正如 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
于 2013-11-23T19:35:29.313 回答
1

isDefinedAton a PartialFunctiongenerated byandThen返回不一致结果的原因是它实际上并未将第一个偏函数应用于其参数,这可能是一项昂贵的操作。

这种行为可能会让人绊倒并且没有记录 - 您可能需要提交一个补丁来为此添加文档。

PS我的猜测是这样做的原因deepTestmatch部分函数定义的源代码中的最外层,并且只有最外层的匹配被考虑用于定义目的 - 但您必须检查源代码scalac可以肯定的是,我想。

于 2013-11-23T19:21:59.587 回答