【问题标题】:Understand Scala match for case classes with two members了解具有两个成员的案例类的 Scala 匹配
【发布时间】:2020-07-09 23:41:23
【问题描述】:

我使用 Scala 已经有一段时间了,但它仍然让我很困扰。我不知道他们为什么把它弄得这么复杂。当这个案例类只有两个成员时,我试图理解匹配的案例类

def main(args: Array[String]): Unit = {

    case class X(a: String, i: Int)
    def doSome(x: X): Unit = {
      x match {
        case "x" X 1 => print("ahhh") // <---- HERE ! 
        case X(_, _) => println("")
      }
    }

    doSome(X("x", 1))


    case class Y(a: String, i: Int, j: Int)

    def doAnother(y:Y): Unit = {
      y match {
        case "y" X 1 => print("ahhh") // how to make similar syntax when there are more than one syntax ?
        case Y(_, _,_) => println("")  // this is understandable
      }
    }
    doAnother(Y("y", 1,2))
  }

"x" X 1 的语法如何匹配X("x",1),如果"x" X 1 可以匹配X("x",1) 那么什么匹配Y("y",1,2),显然"y" Y 1 Y 2 不起作用?

如果我们可以匹配 "y" Y (1,2),那么第一个参数有什么特别之处?

【问题讨论】:

  • case "y" Y (1,2) =&gt; ...
  • @jwvh 您可以在中缀运算符中使用超过 2 个参数吗?编译器如何知道你想给它 3 个参数并且你不是要给它一个元组? (Y("y", (1, 2)))
  • 这能回答你的问题吗? Scala match decomposition on infix operator

标签: scala pattern-matching


【解决方案1】:

至少在List 的情况下,我感觉更自然,例如考虑

List(42, 11) match {
  case head :: tail =>
  case Nil =>
  
}

相对

List(42, 11) match {
  case ::(head, tail) =>
  case Nil =>
  
}

其中head :: tail 直接传达List 的形状。

作为旁注,中缀符号有时可以更清楚地传达意图,例如,考虑广义约束的语法

implicitly[List[Int] <:< Iterable[Int]]    // infix type notation seems more natural
implicitly[<:<[List[Int], Iterable[Int]]]

【讨论】:

    【解决方案2】:

    您不必因为语言功能的存在而使用它。

    在这种情况下,我认为没有理由不使用标准类匹配版本:

    x match {
        case X("x", 1) => print("ahhh")
        case _ => println("")
      }
    }
    
    y match {
      case Y("y", 1, _) => print("ahhh")
      case _ => println("")
    }
    

    【讨论】:

    • 好吧,我不知道。但是,人们的语气正在这样做,这是混乱的,这就是我想理解它的原因。您实际上指的是什么功能?
    • 我将"x" X 1 语法称为X("x", 1) 的同义词。我认为我以前从未在 StackOverflow 上看到过,甚至不知道这是一个选项。
    • 查看其他答案,我意识到我一直都在看到它,但我根本没有认出它(例如在case head :: tail =&gt; 中),这真的很有趣。但我仍然会说,除非有强大的用例,否则最好避免使用它。
    【解决方案3】:

    好的,所以我正在寻找的东西叫做“中缀类型”。来自Scala for Impatient, 2nd edition

    中缀类型是有两个类型参数的类型,写成“中缀” 语法,类型参数之间的类型名称。例如, 你可以写 String Map Int 而不是 Map[String, Int] 的 中缀符号在数学中很常见。例如,A × B = { (a, b) | a Œ A, b Œ B } 是具有类型 A 和 B 的分量的对的集合。 在 Scala 中,这种类型写为 (A, B)。如果您更喜欢 数学符号,你可以定义类型 ×[A, B] = (A, B) 然后你 可以写成 String × Int 而不是 (String, Int)。所有中缀类型 运算符具有相同的优先级。与常规操作员一样,他们 是左结合的,除非它们的名字以 : 结尾。例如,
    String × Int × Int 表示 ((String, Int), Int)。这种类型类似 到,但不一样,作为 (String, Int, Int),它不能是 在 Scala 中以中缀形式编写。

    【讨论】:

      【解决方案4】:

      回答您关于如果我们可以匹配“y”Y (1,2) 的第一个参数有什么特别之处?:这是因为您的案例类如何通过以下方式分解它的unapply 方法。

      与案例类匹配的首选方式是您在两个方法的第二个语句中所做的事情。

      但是,对于 Y,首选的匹配方式是 case Y("y", 1, 2),如 Tim 的评论中所述。

      对于X,您可以通过以下几种方式使用模式匹配的强大功能(对于Y 类似):

      case X("x", 1) => ???
      case X(a, 1) => ???
      case X(_, 1) => ???
      case X("x", _) => ???
      case x@X("x", _) => 
      case X(a, b) if b > 5 => ???
      

      然而,以下是一种非常糟糕的风格,因为它损害了代码的可读性和可维护性

      case "x" X 1 => print("ahhh")
      

      正如 Mario 所提到的,您使用模式匹配的方式更适合于列表而不是案例类,因为它使您的代码与列表的结构方式(head cons tail)保持一致,从而增强了可读性。

      您可以阅读以下文章,以更深入地了解如何利用 scala 模式匹配的强大功能:

      【讨论】:

      • 是的,我同意,但它被 Scala Parser 库使用,如 ^^ {case a~b => .... 而 ~ 是一个有两种类型的案例类。
      猜你喜欢
      • 1970-01-01
      • 2017-04-25
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 2019-03-09
      • 1970-01-01
      • 2017-02-25
      • 1970-01-01
      相关资源
      最近更新 更多