Scala orElse如何处理部分函数

Scala orElse如何处理部分函数,scala,functional-programming,read-eval-print-loop,partialfunction,Scala,Functional Programming,Read Eval Print Loop,Partialfunction,使用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

使用
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

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

{
  case x: Nothing => x
}
(此类型检查,因为
Nothing
a
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
可以理解为简单地将两个
PartialFunction
s中的case语句列表串联起来。因此,在您的情况下,
a.orElse(PartialFunction.empty[Any,Unit]
表示:

{ case "hello" => println("Bye") } orElse { /* no cases here */ }
这简化为:

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

因此,
匹配错误
是显而易见的

注意,还提到
empty
总是抛出
MatchError


据我猜测,您需要一个始终匹配的
PartialFunction
。标准库中没有为此命名的方法,但为什么应该有。您只需编写

{ case _ => () }

您正在使用
PartialFunction
对象应用方法,该方法的定义如下:

def apply[A, B](f: A => B): PartialFunction[A, B] = { case x => f(x) }
基本上,它采用函数形式
a
B
,并自动将其包装在case语句中,问题是您也在传递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
scala>vala:PartialFunction[字符串,单位]={
|案例“你好”=>println(“再见”)
|   }
a:部分函数[字符串,单位]=
scala>val b:PartialFunction[字符串,单位]={
|案例=>println(“默认”)
|   }
b:部分函数[字符串,单位]=
scala>b(“123”)
违约

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

您的尝试有一些错误,但首先让我们看看一个可行的实现:

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
如何定义它:

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) }
您将看到,它为任何
x
定义了一个分部函数,并将给定的
f
函数应用于它。现在,您的
{case“hello”=>println(“bye”)}
f
参数,因此您大概会得到以下(显然出乎意料的)
分部函数

val wrong: PartialFunction[String, Unit] = {
  case x => x match {
    case "hello" => println("bye")  
  }
因此,当您询问它是否已定义时,它将始终返回true,因为它是为任何字符串定义的:

wrong.isDefinedAt("hello") // true (ok)
wrong.isDefinedAt("whatever") // true (sure?)
但是当你尝试应用它时

wrong("hello") // bye (ok)
wrong("whatever") // MatchError (BOOM!)
你在内部比赛中落后了

由于
orElse
根据
已定义的结果决定是否调用“else”,因此它失败的原因显而易见

2.空无一物! 直截了当地说:

def empty[A,B]:部分功能[A,B]

域为空的分部函数。任何调用空分部函数的尝试都会引发
scala.MatchError
异常

您要查找的
PartialFunction
(它不再是真正的部分功能)是:

val fallback: PartialFunction[Any, Unit] = { case _ => println("fallback") }
或者——只是为了表明我们从错误中吸取了教训-

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

如果我定义
valb:PartialFunction[String,Unit]=a.orElse(PartialFunction[String,Unit]{case}=>println(“123”)}
,如果我定义
b(“123”),我仍然会得到匹配异常
,无论如何,我认为你是对的,
PartialFunction.empty
匹配
Nothing
@EndeNeu,你是对的,有两个错误。这个答案抓住了第二个,但是
匹配错误发生了before@GabrielePetronella如果我在评论中定义了
b
,那么
Nothing
beca就没有匹配项使用我不再使用
PartialFunction.empty
了,我有两个单独的PF,一个匹配
“hello”
和另一个匹配的
\u
@EndeNeu正如我在回答中解释的,
a
定义不正确,这就是问题所在。你说的对,问题在于apply方法。你在任意x上定义了一个部分函数,然后在该x上应用给定的函数。有关详细解释,请参阅我的回答。为什么我会这样做s是
c
PartialFunction[String,Unit]
的类型,而不是
PartialFunction[Any,Unit]
?@欠准确,如果你看一下
orElse
的定义,它实际上应该拒绝将
b
作为一个参数,因为
Any
不是
String
的子类型。它以某种方式将
b
视为
PartialFunction[字符串,单位]
并使其工作。无论如何,
orElse
返回的类型将永远不会比原始函数所组成的更宽。@Underveragent,我已经对此进行了一段时间的推理,我必须说我感到困惑。
orElse
肯定会拒绝
b
。我问了一个问题,看看是否有人能理解这一点输出:
wrong("hello") // bye (ok)
wrong("whatever") // MatchError (BOOM!)
val fallback: PartialFunction[Any, Unit] = { case _ => println("fallback") }
val fallback = PartialFunction[Any, Unit] { _ => println("fallback") }