【问题标题】:Magic PartialFunction in ScalaScala中的魔术PartialFunction
【发布时间】:2013-11-14 08:40:20
【问题描述】:

我不认为这段代码应该有效,但它确实有效(在 Scala 2.10 中):

scala>     ((i: Int) => i.toString match {
     |        case s if s.length == 2 => "A two digit number"
     |        case s if s.length == 3 => "A three digit number"
     |     }): PartialFunction[Int,String]
res0: PartialFunction[Int,String] = <function1>

// other interactions omitted

scala> res0.orElse(PartialFunction((i: Int) => i.toString))
res5: PartialFunction[Int,String] = <function1>

scala> res5(1)
res6: String = 1

它是如何工作的?我希望MatchError 被扔进res0

Scala 语言规范似乎没有明确说明应该如何解释 res0

【问题讨论】:

    标签: scala partialfunction


    【解决方案1】:

    诀窍在于编译器不会将您的定义解释为转换为部分函数的总函数——它实际上是首先创建一个部分函数。您可以通过注意 res0.isDefinedAt(1) == false 来验证。

    如果你真的将一个总函数转换为一个部分函数,​​你会得到你所期望的行为:

    scala> PartialFunction((i: Int) => i.toString match {
         |       case s if s.length == 2 => "A two digit number"
         |       case s if s.length == 3 => "A three digit number"
         |     })
    res0: PartialFunction[Int,String] = <function1>
    
    scala> res0 orElse ({ case i => i.toString }: PartialFunction[Int, String])
    res1: PartialFunction[Int,String] = <function1>
    
    scala> res1(1)
    scala.MatchError: 1 (of class java.lang.String)
    // ...
    

    在此示例中,PartialFunction.apply 将其参数视为一个总函数,因此有关其定义位置的任何信息都将丢失。

    【讨论】:

      【解决方案2】:

      orElsePartialFunction 上定义,因此在未定义原始参数的情况下,该参数被视为后备。见the API

      【讨论】:

      • 这并不能解释为什么res0 不会抛出MatchError
      • 如果您阅读@RobinGreen 链接的文档,确实可以做到这一点:Composes this partial function with a fallback partial function which gets applied where this partial function is not defined. orElse 返回一个新的部分函数,​​该函数为res0 或@ 的任何值定义987654329@ 已定义。
      【解决方案3】:

      你说如果res0不匹配,你想试试你的其他pf。这基本上是如何工作的:

      if (res0.isDefinedAt(1)) {
        res0(1)
      } else {
        other(1)
      }
      

      orElse 调用创建了一个OrElse 的实例,它继承自PartialFunctionhttps://github.com/scala/scala/blob/master/src/library/scala/PartialFunction.scala#L159

      当您现在在此OrElse 上调用apply 时,它将调用f1.applyOrElse(x, f2)https://github.com/scala/scala/blob/master/src/library/scala/PartialFunction.scala#L162

      这将调用if (isDefinedAt(x)) apply(x) else f2(x): https://github.com/scala/scala/blob/master/src/library/scala/PartialFunction.scala#L117-L118

      因此,当两个 pf 都不匹配时,您只会得到 MatchError

      【讨论】:

      • 我假设我尝试从res0 中的总函数生成部分函数会导致isDefinedAt 始终返回true
      • 没错,但是你为什么期待MatchError呢?
      • 因为它实际上是一个包含匹配项的总函数,伪装成部分函数,​​或者我是这么认为的。
      • 但是如果你有一个PartialFunction[Int, String] 它已经说“我只接受整数”。如果你现在也没有守卫,哪里不应该匹配?
      • 如果你只调用res0,那么你会得到一个MatchError,但是回退到你的总函数,它会成功。我不明白你为什么会期待别的东西。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-10
      • 2010-09-18
      • 1970-01-01
      相关资源
      最近更新 更多