如何检查函数是否在 Scala 中是偏函数?
How to check if function is partial in Scala?
我有一个接收函数的方法,但该函数可能是部分函数,在这种情况下我不希望它因 MatchError 而失败。
def doSomething[X,Y](opt:Option[X])(f:X=>Y)={
f match {
case p:PartialFunction[X,Y]=> opt.flatMap(p.lift) //This doesn't seem to work
case _ => opt.map(f)
}
}
这样我就可以用这样的方法
doSomething(x){
case t if predicate(t) => otherMethod(t)
}
所以如果我没有谓词,我可以像这样使用它
这个 doSomething(x)(otherMethod)
而不是
doSoemthing(x){
case t=> otherMethod(t)
}
注意:寻找不需要捕获 MatchError
异常的解决方案
我不确定你作为偏函数传递的是什么,但你肯定必须用这样的特定签名来定义它:
val positive: PartialFunction[Int, Option[Int]] = {
case x if x >= 0 => Some(x)
case _ => None
positive
函数仅为正数定义。在负数的情况下,函数 returns None 并且你不会在运行时得到 scala.MatchError。
此特定函数使您能够访问 isDefinedAt
方法,该方法动态测试值是否在函数域中。
postive(5).isDefinedAt // true
poistive.isInstanceOf[PartialFunction[Int, Option[Int]]] // true
我在这里演示了为什么你在检查 p.isInstanceOf
时总是得到 false
def doSomething[X,Y](opt:Option[X])(f:X=>Y)={
f match {
case p if p.isInstanceOf[PartialFunction[X,Y]] =>
println("I'm a pf")
println(s"Is it PartialFunction: ${p.isInstanceOf[PartialFunction[X,Y]]}")
opt.map(p)
case _ =>
println("I'm not a pf")
opt.map(f)
}
}
doSomething[Int, Option[Int]](Some(5))(positive) // partial function case
doSomething[Int, String](Some(5)) { // tricky case
case s => s.toString
}
可以玩玩here:
这不是答案,因为我不认为你想要的在 Scala 中是可能的。
原来的方法很好,按预期工作,虽然它可以更简单一点:
def doSomething[X, Y](opt: Option[X])(f: X => Y): Option[Y] = {
f match {
case p: PartialFunction[X, Y] => opt.collect(p)
case _ => opt.map(f)
}
}
问题出在这里:
doSomething(x){
case t if predicate(t) => otherMethod(t)
}
Scala 正在从 match
表达式创建 Function
而不是 PartialFunction
,因此测试失败。如果你传递一个真实的 PartialFunction
该方法工作正常。
val p: PartialFunction[Int, Int] = {
case i: Int if i > 0 => i
}
doSomething(Some(0))(p) // Returns None
我不认为有任何方法可以做你想做的事,主要是因为 doSomething
有多个参数列表,这会扰乱第二个参数列表的类型推导。
我的建议就是使用
x.map(f)
或
x.collect{
case ...
}
在调用代码中适当。
从 SLS 8.5 的 2.9 开始,部分函数的语法发生了变化,因此即使您这样做 { case x => y}
,也不意味着它是部分函数。它的类型将与您定义的完全一样。
在你的例子中,你将它定义为 X=>Y
(在你的函数参数中),所以它只是一个 X=>Y
(它被编译成一个常规函数,并且不匹配的情况将throw MatchError),即使你这样做 isInstanceOf[PartialFunciton[_,_]]
,它也不会匹配。
为了使您的方案正常运行,您只需将传递的函数转换为 PartialFunction,例如:
doSomething(Some(1))({case 2 => 0}: PartialFunction[Int,Int]) //This returns None without MatchError
而
doSomething(Some(1)){case 2 => 0} //This gives MatchError and it is not recognized as PartialFunction inside the body
这可能不像您想象的那么方便,但这是使其工作的唯一方法。 (或者你为这两种情况定义了 2 个单独的函数,比如标准库中的 collect
和 map
)
我有一个接收函数的方法,但该函数可能是部分函数,在这种情况下我不希望它因 MatchError 而失败。
def doSomething[X,Y](opt:Option[X])(f:X=>Y)={
f match {
case p:PartialFunction[X,Y]=> opt.flatMap(p.lift) //This doesn't seem to work
case _ => opt.map(f)
}
}
这样我就可以用这样的方法
doSomething(x){
case t if predicate(t) => otherMethod(t)
}
所以如果我没有谓词,我可以像这样使用它
这个 doSomething(x)(otherMethod)
而不是
doSoemthing(x){
case t=> otherMethod(t)
}
注意:寻找不需要捕获 MatchError
异常的解决方案
我不确定你作为偏函数传递的是什么,但你肯定必须用这样的特定签名来定义它:
val positive: PartialFunction[Int, Option[Int]] = {
case x if x >= 0 => Some(x)
case _ => None
positive
函数仅为正数定义。在负数的情况下,函数 returns None 并且你不会在运行时得到 scala.MatchError。
此特定函数使您能够访问 isDefinedAt
方法,该方法动态测试值是否在函数域中。
postive(5).isDefinedAt // true
poistive.isInstanceOf[PartialFunction[Int, Option[Int]]] // true
我在这里演示了为什么你在检查 p.isInstanceOf
def doSomething[X,Y](opt:Option[X])(f:X=>Y)={
f match {
case p if p.isInstanceOf[PartialFunction[X,Y]] =>
println("I'm a pf")
println(s"Is it PartialFunction: ${p.isInstanceOf[PartialFunction[X,Y]]}")
opt.map(p)
case _ =>
println("I'm not a pf")
opt.map(f)
}
}
doSomething[Int, Option[Int]](Some(5))(positive) // partial function case
doSomething[Int, String](Some(5)) { // tricky case
case s => s.toString
}
可以玩玩here:
这不是答案,因为我不认为你想要的在 Scala 中是可能的。
原来的方法很好,按预期工作,虽然它可以更简单一点:
def doSomething[X, Y](opt: Option[X])(f: X => Y): Option[Y] = {
f match {
case p: PartialFunction[X, Y] => opt.collect(p)
case _ => opt.map(f)
}
}
问题出在这里:
doSomething(x){
case t if predicate(t) => otherMethod(t)
}
Scala 正在从 match
表达式创建 Function
而不是 PartialFunction
,因此测试失败。如果你传递一个真实的 PartialFunction
该方法工作正常。
val p: PartialFunction[Int, Int] = {
case i: Int if i > 0 => i
}
doSomething(Some(0))(p) // Returns None
我不认为有任何方法可以做你想做的事,主要是因为 doSomething
有多个参数列表,这会扰乱第二个参数列表的类型推导。
我的建议就是使用
x.map(f)
或
x.collect{
case ...
}
在调用代码中适当。
从 SLS 8.5 的 2.9 开始,部分函数的语法发生了变化,因此即使您这样做 { case x => y}
,也不意味着它是部分函数。它的类型将与您定义的完全一样。
在你的例子中,你将它定义为 X=>Y
(在你的函数参数中),所以它只是一个 X=>Y
(它被编译成一个常规函数,并且不匹配的情况将throw MatchError),即使你这样做 isInstanceOf[PartialFunciton[_,_]]
,它也不会匹配。
为了使您的方案正常运行,您只需将传递的函数转换为 PartialFunction,例如:
doSomething(Some(1))({case 2 => 0}: PartialFunction[Int,Int]) //This returns None without MatchError
而
doSomething(Some(1)){case 2 => 0} //This gives MatchError and it is not recognized as PartialFunction inside the body
这可能不像您想象的那么方便,但这是使其工作的唯一方法。 (或者你为这两种情况定义了 2 个单独的函数,比如标准库中的 collect
和 map
)