如何进行多参数列表类的匹配模式?

21
考虑下面这个类:

class MyClass {
  public int myInt;
  public string myString;
}

class DateTime(year: Int, month: Int, day: Int)(hour: Int, minute: Int, second: Int)

如果我想匹配像这样的内容,那么unapply方法会是什么样子:

dt match {
  case DateTime(2012, 12, 12)(12, _, _) => // December 12th 2012, 12 o'clock
  /* ... */
}

我尝试了这个:

def unapply(dt: DateTime) = 
  Some((dt.year, dt.month, dt.day),(dt.hour, dt.minute, dt.second))

但那真的没有起作用。

3个回答

25

案例类只在第一组参数上进行匹配(并执行它们的其他巧妙操作):

scala> case class A(i: Int)(j: Int) { }
defined class A

scala> A(5)(4) match { case A(5) => "Hi" }
res14: java.lang.String = Hi

scala> A(5)(4) == A(5)(9)
res15: Boolean = true
如果它不是一个case class,你可以定义unapply为任何你想要的东西,因此这取决于类的实现者。默认情况下,没有unapply,所以你只能匹配类型。
如果你想使用方便的case class特性来匹配和比较所有内容,但是有某种分割,你可以嵌套case classes:
case class Time(hour: Int, minute: Int, second: Int) { }
case class Date(year: Int, month: Int, day: Int) { }
case class DateTime(date: Date, time: Time) { }

scala> val dt = DateTime(Date(2011,5,27), Time(15,21,50))
scala> dt match { case DateTime(Date(2011,_,_),Time(h,m,50)) => println(h + ":" + m) }
15:21

10

在 Rex 的回答基础上,不仅你只能对第一个参数块进行模式匹配,而且这种行为是非常有意设计的。

更有趣的问题是,作为代数数据类型,为什么案例类甚至支持多个参数列表...

没有足够强的理由为案例类添加特殊行为,而多个参数列表被证明非常有用。在生产代码中,此功能通常仅用于提供隐式参数,自然地,您不希望对其进行模式匹配。


5
多参数列表很好,因为您可以有多个可变参数列表。 - Paul Draper
也许这是一个有点牵强的例子,但如果你能够说 case class MyCase[A <: WithBar](a: A)(b: A#Bar) 并且从中获得所有 "nifty features",那么就可以像 val x = MyCase(FooWithBar(...))(Bar(...)) 这样创建新实例,而不需要为 A 进行类型注释。使用 MyCase[A <: WithBar](a: A, b: A#Bar) 这样的签名,你必须用 val x = MyCase[FooWithBar](FooWithBar(...), Bar(...)) 实例化,因为当在与类型 A 的参数列表中指定 A#Bar 参数时,类型推断无法工作。 - falconepl
避免使用 case class 的另一个原因是它们与语言的许多高级特性(或概念)不兼容。 - matanster

1
可能没有起作用是因为Scala没有逗号运算符,并且您从提取器中返回了Some((a,b),(x,y))。如果您改用Some(((a,b,c),(x,y,z)))(即Tuple2[Tuple3[A,B,C],Tuple3[X,Y,Z]]),我认为它可能会起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接