如何检查函数是否在 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 个单独的函数,比如标准库中的 collectmap