【问题标题】:Scala named extractor for class with one parameterScala 为具有一个参数的类命名提取器
【发布时间】:2017-09-03 08:26:03
【问题描述】:

Scala 命名提取器示例:

class Foo1(n: Int) {
  def isEmpty: Boolean =
    false
  def get: Foo1 =
    this
  def _1: Int   = n
}

object Foo1 {
  def unapply(arg: Foo1) = arg
}

class Foo2(n: Int, nn: Int) {
  def isEmpty: Boolean =
    false
  def get: Foo2 =
    this
  def _1: Int   = n
  def _2: Int   = nn
}

object Foo2 {
  def unapply(arg: Foo2) = arg
}

object Main extends App {

  new Foo1(1) match {
    case Foo1(n) if n > 0 ⇒ println(n)
  }
  //Error: value > is not a member of Foo1
  //case Foo1(n) if n > 0 ⇒ println(n)

  new Foo2(1, 2) match {
    case Foo2(n, nn) if n > 0 ⇒ println(s"$n -> $nn")
  }
  // 1 -> 2

}

我很好奇为什么 Scala 会为一个参数 (Foo1) 提取类实例,但如果参数的数量 > 1 则提取参数 (Foo2(n,nn))。我是否遗漏了什么或它的预期行为?

【问题讨论】:

    标签: scala pattern-matching


    【解决方案1】:

    我认为,引入基于命名的提取器的主要原因是可以使用值类(扩展 AnyVal)来提取单个值,而不是 def unapply(arg: Foo1): Options[Foo1]。这样你就可以避免额外的Option 的实例化,从而避免垃圾收集。通常,这用于提取满足特定条件的值。

    例子:

    class PositiveNonZeroInteger(private val n: Int) extends AnyVal {
      def isEmpty: Boolean = n <= 0
      def get: Int = n
    }
    
    object PositiveNonZeroInteger {
      def unapply(arg: Int) = new PositiveNonZeroInteger(arg)
    }
    
    1 match {
      case PositiveNonZeroInteger(pos) => "yay"
      case _                           => "nay"
    } // => "yay"
    
    0 match {
      case PositiveNonZeroInteger(pos) => "yay"
      case _                           => "nay"
    } // => "nay"
    

    看来,元组 getter (_1, _2) 可能只有在至少存在两个时才被编译器解析。

    因此,如果您将第一个 foo 重写为:

    class Foo1(private val n: Int) /*extends AnyVal*/ {
      def isEmpty: Boolean = false
      def get: Int = n
    }
    
    object Foo1 {
      def unapply(arg: Foo1) = arg
    }
    

    您的代码可以工作,但这不是基于名称的提取器的用途。

    希望这会有所帮助。

    【讨论】:

      【解决方案2】:

      Scala 语言规范在提取器模式中使用 0、1 或多个参数来区分情况:

      http://scala-lang.org/files/archive/spec/2.12/08-pattern-matching.html#extractor-patterns

      对于 1 个参数,它需要匹配类型的选项,即 Foo1 的情况下为 Option[Int],以及多个参数的元组的 Option

      在您的示例中,如果您想从 unapply 方法返回实例本身,您可以通过实现来避免输入 Foo1 预期的 Option[Int]

      class Foo1(n: Int) {
        def get: Int = n
        ...
      

      【讨论】:

      • 我尽量避免 Option 额外创建
      • 然后使用get函数直接返回int,如我的回答中所述。
      猜你喜欢
      • 2016-05-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-26
      • 2014-03-13
      • 1970-01-01
      相关资源
      最近更新 更多