【问题标题】:Scala pattern matching with combinations over parameter listScala模式匹配参数列表的组合
【发布时间】:2016-06-28 20:39:12
【问题描述】:

我有一个案例类Pair(a: Int, b: Int),它代表一对 2 个整数。为了有Pair(2, 5) == Pair(5, 2),我重写了equals方法如下。

override def equals(that: Any): Boolean = that match {
  case Corner(c, d) => (a == c && b == d) || (a == d && b == c)
  case _ => false
}

现在等式成立,Pair(2, 5) == Pair(5, 2) 返回真,就像我想要的那样。但是,这在模式匹配时不起作用:

Pair(2, 5) match {
  case Pair(5, 2) => print("This is what I want")
  case _ => print("But this is what I get")
}

有人可以帮助我吗?我可以/应该这样做吗?有哪些替代方案?我真的不想每次与pair进行模式匹配时都写case Pair(2, 5) | case(5, 2) =>

【问题讨论】:

标签: scala parameters pattern-matching


【解决方案1】:

简答:
那是行不通的。不过,您可以像这样修改您的陈述:

Pair(2, 5) match {
  case that if that == Pair(5, 2) => println("This is what I want")
  case _ => println("nope")
}

长答案:
当你match 在一个案例类上时,它没有使用equals;它实际上是使用伴随对象的unapply 方法。事实上,scala 的match/case 语句背后的大部分“魔法”归结为unapply

当您写case class Pair(a: Int, b: Int) 时,您会免费获得很多东西。其中之一是an extractor,例如:

object Pair {
  def unapply(pair: Pair): Option[(Int, Int)] = {
    Some((pair.a, pair.b))
  }
}

所以当你说pair match { case Pair(a, b) => ... }时,编译器认为Pair.unapply(pair),然后将a分配给结果元组的_1位置的值,并将b分配给_2中的值位置。 (如果Pair.unapply(pair) 返回None,则该案例将失败)。

基本上,您只能从提取器的每个输入中获取一个特定值,但您要查找的内容需要两个。

【讨论】:

    【解决方案2】:

    我想说这种情况的一个好的解决方案可能不是覆盖equals,而是禁止无序对:

    case class Pair(a: Int, b: Int) {
      assert(a <= b, s"First parameter of Pair($a, $b) must be less or equal to the second")
    }
    
    object Pair {
      def of(a: Int, b: Int) = if (a <= b) Pair(a, b) else Pair(b, a)
    }
    

    很遗憾,Scala 不允许在案例类的伴随对象中“覆盖”apply 方法,否则这将是一个非常透明的解决方案。

    【讨论】:

      【解决方案3】:

      正如Alexey Romanov 所述,无法覆盖案例类的 unapply 方法。然而,就我而言,我不需要 Pair 成为案例类,所以首先我这样做了:

      class Pair(val a: Int, val b: Int)
      object Pair {
        def apply(a: Int, b: Int) = new Pair(a, b)
      
        def unapply(arg: Pair): Option[(Int, Int)] = {
          if (arg.a < arg.b)
            Some(arg.a, arg.b)
          else
            Some(arg.b, arg.a)
        }
      }
      
      Pair(5, 2) match {
        case Pair(3, 4) => "It's dumb"
        case Pair (2, 5) => "Matched!"
        case _ => "Doesn't work"
      }
      
      Pair(2, 5) match {
        case Pair(3, 4) => "It's dumb"
        case Pair (2, 5) => "Matched!"
        case _ => "Doesn't work"
      }
      
      Pair(2, 5) match {
        case Pair(4, 3) => "It's dumb"
        case Pair(5, 2) => "Doesn't match!"
        case _ => "Should have written the digits in ascending order!"
      }
      

      可以看出,它并不完美——我仍然必须确保用数字升序编写匹配案例。然而,一旦我意识到通过简单地断言这些值在构造函数中是有序的,我既可以使 equals 方法更短,也可以让 apply 方法负责排序,或者只是按照 Alexey Romanov 的建议做,这就是为什么我将他的答案标记为已接受的原因。

      【讨论】:

      • 请注意,实现equals 的类也必须覆盖hashCode,以使相等的实例始终具有相同的哈希码(这也适用于您的原始问题)。 case class 自动处理这种一致性(前提是所有参数本身在这方面都是正确的)。
      猜你喜欢
      • 2023-03-06
      • 2018-10-01
      • 1970-01-01
      • 2015-01-17
      • 1970-01-01
      • 2023-03-21
      • 2012-10-14
      • 2017-10-14
      • 2016-05-12
      相关资源
      最近更新 更多