【问题标题】:Adapter for a PartialFunctionPartialFunction 的适配器
【发布时间】:2016-05-18 11:11:26
【问题描述】:

我正在尝试想出一个组合器,它可以让我做这样的事情:

 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 的事情。显然,最后一次尝试不起作用,因为它的结果总是被定义的,并且只会在不存在的键上失败,我只是用它来说明我正在寻找的解决方案的理想外观。

有什么想法吗?

【问题讨论】:

  • 所以如果密钥不存在,你可以例外吗?还是我错过了什么?
  • 不,我想要一个在元组上定义的 PartialFunction,它的第一个元素作为映射中的键出现。没有例外:)
  • 那它是怎么偏的呢?当第一个元素不存在时,期望的结果是什么?
  • 像任何偏函数一样:如果您使用其域之外的值调用它,则会出现异常。所以,从这个意义上说,是的,我可以接受 :)。这个想法,但是我们永远不要在这些值上调用它,因为它会正确定义 liftisDefinedAtorElse 等。
  • 啊哈!那么,if(pf.isDefinedAt(a)) => pf(a)=> pf(a) 不完全一样吗?如果 pf 在 a 处定义,两者都将起作用,如果没有,则给出异常

标签: scala partialfunction


【解决方案1】:

函数本身 (pf.apply) 并没有真正评估两次,但它的 isDefinedAt 评估了两次以成功匹配您的定义。这意味着在初始 PartialFunction pf 中评估两次 unapply-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 的文档指出:

对于所有偏函数字面量,编译器生成一个 避免重复评估模式的 applyOrElse 实现 匹配器和守卫。这使得 applyOrElse 成为 许多操作和场景的高效实现,例如:

...

  • lift 和 unlift 不会在每次调用时两次评估源函数

【讨论】:

  • 谢谢你指点我.unlift,我不知道。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-05
  • 1970-01-01
  • 2017-08-29
  • 2011-07-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多