PartialFunction的适配器

时间:2016-05-18 11:11:27

标签: scala partialfunction

我正在努力想出一个能让我做这样的事情的组合器:

 def pfAdapter(pf: PartialFunction[String, String]): PartialFunction[(String,String), String]) = { 
   case (a,b) if(pf.isDefinedAt(a)) => pf(a)
 }

基本上,我有一个Map[String, String],并且希望将它用作带有两个参数的PartialFunction - 忽略第二个元素,并使用第一个元素作为键。

上述方法有效,但我不喜欢pf基本上被评估两次(可能没有办法解决这个问题),而且它不是"优雅" ......我想知道是否有某种组合器,我不知道这会让我做{ _._1 } andThen pf之类的事情。显然,这最后的尝试并不起作用,因为它的结果总是被定义,并且只会在不存在的键上失败,我只是用它来说明我正在寻找的解决方案如何理想地看起来

想法,有人吗?

1 个答案:

答案 0 :(得分:2)

函数本身(pf.apply)实际上没有被评估两次,但它的isDefinedAt 被评估两次,以便与您的定义成功匹配。这意味着在初始unapply PartialFunction中评估两次pf - s和守卫。

顺便说一句,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的文档是:{/ p>

  

对于所有部分函数文字,编译器生成一个   applyOrElse实现避免了对模式的双重评估   匹配和守卫。这使得applyOrElse成为了基础   许多操作和方案的有效实施,例如:

     

...

     
      
  • 提升和解除不会在每次调用时评估两次源函数
  •