【问题标题】:How to pattern match large Scala case classes?如何对大型 Scala 案例类进行模式匹配?
【发布时间】:2019-09-13 19:40:14
【问题描述】:

考虑以下 Scala 案例类:

case class WideLoad(a: String, b: Int, c: Float, d: ActorRef, e: Date)

模式匹配允许我提取一个字段并丢弃其他字段,如下所示:

someVal match {
    case WideLoad(_, _, _, d, _) => d ! SomeMessage(...)
}

我想做的,以及当一个案例类有大约 20 个奇数字段时更相关的是,以不涉及输入 WideLoad(_, _, _, _, _, some, _, _, _, thing, _, _, interesting) 的方式仅提取几个值。

我希望命名 args 可以在这里提供帮助,尽管以下语法不起作用:

someVal match {
    case WideLoad(d = dActor) => dActor ! SomeMessage(...)
    //              ^---------- does not compile
}

这里有什么希望吗,还是我卡住了很多很多_, _, _, _

编辑:我知道我可以做到case wl @ WideLoad(...whatever...) => wl.d,但我仍然想知道是否有更简洁的语法可以满足我的需要,而无需引入额外的val

【问题讨论】:

  • 即使有 4 或 5 个字段,所有的下划线都会使其难以阅读。命名参数语法会大大提高可读性,但据我所知,目前还没有这样的语法。
  • 我的印象是长参数列表通常是要避免的。
  • 你的意思是 case WideLoad (d == dActor)
  • 有一个关于这个问题的 SIP:issues.scala-lang.org/browse/SI-6524
  • @JamesMoore 关于这个问题,我想知道是否可以从 import 语句的语法中获得灵感。我们有 import org.apache.{foo,bar=>bat} 导入 foo 和 bar,将后者重命名为 bat。以同样的方式我们可以做到case WideLoad{d=>actorRef} if actorRef == 42 => SomeMessage(...)

标签: scala pattern-matching


【解决方案1】:

我不知道这是否合适,但您也可以构建一个对象来匹配该字段或该组字段(未经测试的代码):

object WideLoadActorRef {
  def unapply(wl: WideLoad): Option[ActorRef] = { Some(wl.d) }
}

someVal match {
  case WideLoadActorRef(d) => d ! someMessage
}

甚至

object WideLoadBnD {
  def unapplySeq(wl: WideLoad): Option[(Int,ActorRef)] = { Some((wl.b,wl.d)) }
}

someVal match {
  case WideLoadBnD(b, d) => d ! SomeMessage(b)
}

【讨论】:

  • 我非常喜欢你的想法。它完全符合我的需要,几乎没有额外的语法,并且明确定义了意图、类型安全等。在我重构此代码以拥有更小的案例类之前,这是一个很好的权宜之计。
【解决方案2】:

你总是可以退回到守卫。这不是很好,但比你的怪物案例类的正常模式匹配更好:-P

case class Foo(a:Int, b:Int, c:String, d:java.util.Date)

def f(foo:Foo) = foo match {
  case fo:Foo if fo.c == "X" => println("found")
  case _ => println("arrgh!")
}

f(Foo(1,2,"C",new java.util.Date())) //--> arrgh!
f(Foo(1,2,"X",new java.util.Date())) //--> found

也就是说,我认为您应该重新考虑您的设计。也许您可以使用案例类、元组、列表、集合或映射在逻辑上将一些参数组合在一起。 Scala 确实支持嵌套模式匹配:

case class Bar(a: Int, b:String)
case class Baz(c:java.util.Date, d:String)
case class Foo(bar:Bar, baz:Baz)

def f(foo:Foo) = foo match {
   case Foo(Bar(1,_),Baz(_,"X")) => println("found")
   case _ => println("arrgh!")
}

f(Foo(Bar(1,"c"),Baz(new java.util.Date, "X"))) //--> found
f(Foo(Bar(1,"c"),Baz(new java.util.Date, "Y"))) //--> arrgh! 

【讨论】:

  • 是的,我正在朝着这个目标进行重构。这样做可能比建造类似于我在问题中提出的沙堡更容易。
【解决方案3】:

您可以只指定匹配模式中的类型:

case class WideLoad(a: String, b: Int, c: Float, d: ActorRef, e: Date)

val someVal = WideLoad(...)

someVal match {
    case w: WideLoad => w.d ! SomeMessage(...)
}

【讨论】:

    【解决方案4】:

    您可以创建一个新的案例类,它是您更大的案例类的摘要

    case class WideLoad(a: String, b: Int, c: Float, d: ActorRef, e: Date)
    case class WideLoadSummary(d: ActorRef, e: Date)
    

    然后像往常一样进行模式匹配。

    val someVal = WideLoadSummary(wideload.d, wideload.e)
    
    someVal match {
        case WideLoadSummary(d, _) => d ! SomeMessage(...)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-25
      • 2013-04-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-25
      • 2016-03-10
      • 1970-01-01
      相关资源
      最近更新 更多