2016-05-18 42 views
1

我想拿出一个组合子,让我做这样的事情:适配器一个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本质上得到两次评估(可能没有办法),并且它不是“优雅”的事实...我想知道是否有一些那种我不知道的combinator会让我做点像{ _._1 } andThen pf。显然,这最后一次尝试不起作用,因为它的结果总是被定义的,并且只会在不存在的键上失败,我只是用它作为我正在寻找的解决方案理想状态下的示例。

想法,任何人吗?

+0

因此,如果密钥不存在,您可以处理异常情况?或者我错过了什么? –

+0

不,我想要一个PartialFunction,它是在元组上定义的,它们的第一个元素作为map中的一个键存在。没有例外:) – Dima

+0

那么它是如何部分?当第一个元素不存在时,期望的结果是什么? –

回答

2

函数本身(pf.apply)是不是真的两次评估,但其isDefinedAt与您定义的成功匹配两次评估。这意味着在初始PartialFunctionpf评估两次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) 

所以第一次执行pfAdapterFunction.unlift((t: (A, T)) => pf.lift(t._1))实际上确实避免两次评估isDefinedAt

这工作,因为Map.collectPartialFunction.applyOrElse实现,并为applyOrElse状态的文档,即:

对于所有的部分功能文本编译器生成的 applyOrElse实现避免了模式 匹配器和双评估警卫。这使得applyOrElse为 高效实现许多操作和场景,如基础:

...

  • 电梯和unlift不会在每次调用两次评估输出功能
+0

感谢您指向'.unlift',我不知道它。 – Dima