函数本身 ( pf.apply
) 并没有真正被评估两次,但它isDefinedAt
被评估两次以成功匹配您的定义。这意味着unapply
在初始时评估两次 -s 和警卫PartialFunction
pf
。
顺便说一句,Scalaz 中有一个组合器可以做类似的事情:pf.first.andThen(_._1)
,但它基本上等同于您的定义。
您可以编写一个小测试,以查看是否pf.isDefinedAt
被评估了两次并使用以下几种可能的实现来运行它pfAdapter
:
object Unapply {
def unapply(s: String): Boolean = {
println(s"unapplying on $s")
s == "1"
}
}
val m = Map("1" -> 1, "2" -> 2)
def pf: PartialFunction[String, String] = {
case Unapply() => "11"
}
def pfAdapter1[A, B, T](pf: PartialFunction[A, B]): PartialFunction[(A, T), B] =
Function.unlift((t: (A, T)) => pf.lift(t._1))
def pfAdapter2[A, B, T](pf: PartialFunction[A, B]): PartialFunction[(A, T), B] =
new PartialFunction[(A, T), B] {
def isDefinedAt(arg: (A, T)) = pf.isDefinedAt(arg._1)
def apply(arg: (A, T)) = pf(arg._1)
}
def pfAdapter3[A, B, T](pf: PartialFunction[A, B]): PartialFunction[(A, T), B] = {
case (a,b) if pf.isDefinedAt(a) => pf(a)
}
def pfAdapter4[A, B, T](pf: PartialFunction[A, B]): PartialFunction[(A, T), B] = {
import scalaz.Scalaz._
pf.first.andThen(_._1)
}
println(m collect pfAdapter1(pf))
println(m collect pfAdapter2(pf))
println(m collect pfAdapter3(pf))
println(m collect pfAdapter4(pf))
而执行这段代码的结果如下:
unapplying on 1
unapplying on 2
List(11)
unapplying on 1
unapplying on 1
unapplying on 2
List(11)
unapplying on 1
unapplying on 1
unapplying on 2
List(11)
unapplying on 1
unapplying on 1
unapplying on 2
List(11)
pfAdapter
所以:的第一个实现Function.unlift((t: (A, T)) => pf.lift(t._1))
实际上确实避免isDefinedAt
了两次评估。
这是有效的,因为Map.collect
是用PartialFunction.applyOrElse
和状态文档实现的applyOrElse
,即:
对于所有部分函数文字,编译器会生成一个 applyOrElse 实现,以避免对模式匹配器和守卫进行双重评估。这使得 applyOrElse 成为许多操作和场景高效实现的基础,例如:
...
- lift 和 unlift 不会在每次调用时两次评估源函数