10

orElse使用定义的方法,我得到了非常奇怪的行为(至少在我看来)PartialFunction

在我看来:

val a = PartialFunction[String, Unit] {
    case "hello" => println("Bye")
}
val b: PartialFunction[Any, Unit] = a.orElse(PartialFunction.empty[Any, Unit])
a("hello") // "Bye"
a("bogus") // MatchError
b("bogus") // Nothing
b(true)    // Nothing

有道理,但这不是它的行为方式,我很难理解为什么类型签名似乎表明了我在上面公开的内容。

以下是我在 Scala 2.11.2 中观察到的记录:

Welcome to Scala version 2.11.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_11).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val a = PartialFunction[String, Unit] {
     | case "hello" => println("Bye")
     | }
a: PartialFunction[String,Unit] = <function1>

scala> a("hello")
Bye

scala> a("bye")
scala.MatchError: bye (of class java.lang.String)
  at $anonfun$1.apply(<console>:7)
  at $anonfun$1.apply(<console>:7)
  at scala.PartialFunction$$anonfun$apply$1.applyOrElse(PartialFunction.scala:242)
  at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:36)
  ... 33 elided

scala> val b = a.orElse(PartialFunction.empty[Any, Unit])
b: PartialFunction[String,Unit] = <function1>

scala> b("sdf")
scala.MatchError: sdf (of class java.lang.String)
  at $anonfun$1.apply(<console>:7)
  at $anonfun$1.apply(<console>:7)
  at scala.PartialFunction$$anonfun$apply$1.applyOrElse(PartialFunction.scala:242)
  at scala.PartialFunction$OrElse.apply(PartialFunction.scala:162)
  ... 33 elided

注意它的返回类型val b并没有扩大PartialFunction的类型。

但这也不能按预期工作:

scala> val c = a.orElse(PartialFunction.empty[String, Unit])
c: PartialFunction[String,Unit] = <function1>

scala> c("sdfsdf")
scala.MatchError: sdfsdf (of class java.lang.String)
  at $anonfun$1.apply(<console>:7)
  at $anonfun$1.apply(<console>:7)
  at scala.PartialFunction$$anonfun$apply$1.applyOrElse(PartialFunction.scala:242)
  at scala.PartialFunction$OrElse.apply(PartialFunction.scala:162)
  ... 33 elided
4

3 回答 3

29

您的尝试有一些问题,但首先让我们看看一个有效的实现:

scala> val a: PartialFunction[String, Unit] = { case "hello" => println("bye") }
a: PartialFunction[String,Unit] = <function1>

scala> val b: PartialFunction[Any, Unit] = { case _ => println("fallback") }
b: PartialFunction[Any,Unit] = <function1>

scala> val c = a.orElse(b)
c: PartialFunction[String,Unit] = <function1>

scala> c("hello")
bye

scala> c("foo")
fallback

您的代码中有两个主要错误:

  1. PF 的定义方式
  2. (错误的)假设empty是一个“包罗万象”的函数,它返回Nothing

1.如何定义PartialFunction

val right: PartialFunction[String, Unit] = {
  case "hello" => println("bye")
}

如何定义它:

val wrong = PartialFunction[String, Unit] {
  case "hello" => println("bye")
}

如果你看一下定义PartialFunction.apply

def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) }

您会看到它为any x定义了一个偏函数,并将给定的f函数应用于它。现在你{ case "hello" => println("bye") }f论点,所以你大约会得到以下结果(显然是出乎意料的)PartialFunction

val wrong: PartialFunction[String, Unit] = {
  case x => x match {
    case "hello" => println("bye")  
  }

因此,当您询问它是否已定义时,它将始终返回 true,因为它是为任何字符串定义的:

wrong.isDefinedAt("hello") // true (ok)
wrong.isDefinedAt("whatever") // true (sure?)

但是当你尝试apply它时

wrong("hello") // bye (ok)
wrong("whatever") // MatchError (BOOM!)

你缺乏内在的匹配。

由于orElse根据 的结果决定是否调用“else” isDefined,那么它失败的原因就很明显了。

2、空无一物!

直接来自文档

def empty[A, B]: PartialFunction[A, B]

具有空域的偏函数。任何调用空部分函数的尝试都会导致抛出scala.MatchError异常。

您正在寻找的PartialFunction(好吧,它不再是部分的)是:

val fallback: PartialFunction[Any, Unit] = { case _ => println("fallback") }

或者——只是为了表明我们从错误中吸取教训——

val fallback = PartialFunction[Any, Unit] { _ => println("fallback") }
于 2014-08-18T23:11:53.667 回答
2

您正在使用PartialFunction这样定义的对象应用方法:

def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) }

基本上它采用函数形式AB自动将其包装在 case 语句中,问题是您也传递了案例,我不能 100% 确定接下来会发生什么,您可以尝试将函数传递给 apply 或轻松您可以尝试不使用 apply 方法:

scala>   val a: PartialFunction[String, Unit] = {
     |     case "hello" => println("Bye")
     |   }
a: PartialFunction[String,Unit] = <function1>

scala>   val b: PartialFunction[String, Unit] =  {
     |     case _ => println("default")
     |   }
b: PartialFunction[String,Unit] = <function1>

scala> b("123")
default

您还可以扩展 trait 并实现apply和,如此isDefined所示。

于 2014-08-18T22:22:33.233 回答
1

PartialFunction.empty[A,B]相当于:

{
  case x: Nothing => x
}

(此类型检查,因为NothingA和的子类型B。)

或者,等效地:

{
  // note: this is probably not a valid Scala code for a partial function
  // but, as PartialFunction.empty's name suggests, it's an *empty* block
} 

这不能匹配任何东西。

.orElse可以理解为简单地连接来自两个PartialFunctions 的 case 语句列表。所以,在你的情况下a.orElse(PartialFunction.empty[Any,Unit]意味着:

{ case "hello" => println("Bye") } orElse { /* no cases here */ }

这简化为:

{ case "hello" => println("Bye") }

或者

{ case "hello" => println("Bye"); case x:Nothing => x }

MatchError因此是显而易见的。

请注意,文档还提到empty总是 throws MatchError


据我猜测,你想要一个PartialFunction总是匹配的。标准库中没有为此命名的方法,但为什么应该有。你可以简单地写

{ case _ => () }
于 2014-08-18T22:22:31.700 回答