【问题标题】:Why Scala PartialFunction works without defining isDefinedAt?为什么 Scala PartialFunction 没有定义 isDefinedAt 就可以工作?
【发布时间】:2019-11-16 11:57:01
【问题描述】:

看起来FirstSecond是一样的,但是为什么呢?

第一

val iter = List(1, 2, 3, 4, 5).iterator
val first = iter.collect(new PartialFunction[Int, Int]{
  def apply(i: Int) = i
  def isDefinedAt(i: Int) = i > 0 && i < 3
})
first.foreach((println(_)))

第二

val iter2 = List(1, 2, 3, 4, 5).iterator
val second = iter2.collect {
  case i:Int if i > 0 && i < 3 => i
}
second.foreach((println(_)))

是不是因为 Scala 编译器会自动转换 { case i:Int if i &gt; 0 &amp;&amp; i &lt; 3 =&gt; i } 转换成 First 的实现形式并从 if i &gt; 0 &amp;&amp; i &lt; 3 部分生成 isDefinedAt

另外,case i:Int if i &gt; 0 &amp;&amp; i &lt; 3 =&gt; i 是 Case 类模式匹配,如果我是正确的。但是,在scala/src/library/scala/PartialFunction.scala 中,PartialFunction 没有 Case 类定义。

trait PartialFunction[-A, +B] extends (A => B)

那么为什么这种案例类模式匹配有效?

我认为 Scala 编译器智能地做了很多隐式工作,但它让我无法理解正在发生的事情以及如何编写 Scala 代码。

如果有好的参考资料,而不是语言或编译器规范,可以理解 Scala代码语法和Scala编写代码的方式,请指教。

【问题讨论】:

  • 不,模式匹配不仅适用于案例类。

标签: scala partialfunction partial-functions


【解决方案1】:

是不是因为 Scala 编译器自动将 { case i:Int if i > 0 && i i } 转换为 First 的实现形式,并从 **if i > 0 && i

是的,确切的翻译在Pattern Matching Anonymous Functions 中给出。就在这里

new PartialFunction[Int, Int]{
  def apply(x: Int) = x match {
    case i:Int if i > 0 && i < 3 => i
  }
  def isDefinedAt(x: Int) = x match {
    case i:Int if i > 0 && i < 3 => true
    case _ => false
  } 
}

注意与apply 中的第一个示例的区别!当isDefined 为假时,您可以调用它。

另外,如果我是正确的,case i:Int if i > 0 && i i 是 Case 类模式匹配

如果有的话,情况正好相反; case 类之所以这样调用,是因为它们可以进行模式匹配,并且模式匹配在 Scala 中使用 case 关键字。

【讨论】:

  • 那里的规范已经过时了,所以我发布了准确的翻译。我将创建一张票,因为仍有足够的时间来更新 Scala 3 的规范,它还没有规范。我还将为一个既是最新的规范创建票证,您可以阅读它以获得乐趣和利润。
【解决方案2】:

是的,编译器会将第二个版本转换为 PartialFunction[Int,Int](因为这是 collect 所采用的)。

这里没有case class匹配,甚至在类型上也不匹配,因为值必须是Int(因此不需要第二版中的类型声明)。

style guide 提供了很多关于如何编写 Scala 的提示。

【讨论】:

    【解决方案3】:

    举个例子

    object Main {
      def f = (1 to 5).collect { case i if i > 0 && i < 3 => i }
    }
    

    编译器生成的偏函数定义了applyOrElse,因为它比幼稚的习惯用法更高效:

    if (pf.isDefinedAt(x)) pf.apply(x) else ???

    显示该实现,类似于规范中描述的内容:

    $ scalac -Vprint:typer pf.scala
    [[syntax trees at end of                     typer]] // pf.scala
    package <empty> {
      object Main extends scala.AnyRef {
        def <init>(): Main.type = {
          Main.super.<init>();
          ()
        };
        def f: IndexedSeq[Int] = scala.Predef.intWrapper(1).to(5).collect[Int](({
          @SerialVersionUID(value = 0) final <synthetic> class $anonfun extends scala.runtime.AbstractPartialFunction[Int,Int] with java.io.Serializable {
            def <init>(): <$anon: Int => Int> = {
              $anonfun.super.<init>();
              ()
            };
            final override def applyOrElse[A1 <: Int, B1 >: Int](x1: A1, default: A1 => B1): B1 = ((x1.asInstanceOf[Int]: Int): Int @unchecked) match {
              case (i @ _) if i.>(0).&&(i.<(3)) => i
              case (defaultCase$ @ _) => default.apply(x1)
            };
            final def isDefinedAt(x1: Int): Boolean = ((x1.asInstanceOf[Int]: Int): Int @unchecked) match {
              case (i @ _) if i.>(0).&&(i.<(3)) => true
              case (defaultCase$ @ _) => false
            }
          };
          new $anonfun()
        }: PartialFunction[Int,Int]))
      }
    }
    

    AbstractPartialFunction 定义的地方

    def apply(x: T1): R = applyOrElse(x, PartialFunction.empty)
    

    这里是an external link 更改为使用applyOrElseimproved PartialFunction 可以追溯到 2012 年。可能该功能没有得到充分的记录或宣传。扩展Scaladoc for PartialFunction 可获得一些信息。出于某种原因,该链接显示orElse,因此您实际上必须向后滚动查看applyOrElse。文档似乎很难。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-14
      • 2014-01-17
      • 1970-01-01
      相关资源
      最近更新 更多