从模式匹配创建元组中的scala MatchError

问题描述

我写了以下代码

case class Filters(city: List[String],isType: List[String])

val input = "BNG:school | HYD:school,restaurant"

val(filter1:Filters,filter2:Filters) = input.split("\\|") match {
  case Array(f1,f2) => (f1.split(":") match {
      case Array(c,t) => t match {
        case _ if t contains "," => Filters(List(c.trim),t.split(",").map(_.trim).toList)
        case _ => Filters(List(c.trim),List(t.trim))
      }
    },f2.split(":") match {
      case Array(c,List(t.trim))
    }
  })
  case _ => (input.split(":") match {
    case Array(x,y) => Filters(List(x.trim),List(y.trim))
  },Nil)
}

它提供以下输出

filter1: Filters = Filters(List(BNG),List(school))
filter2: Filters = Filters(List(HYD),List(school,restaurant))

但是,如果我将输入更改为:val input = "BNG:school"

输出显示错误

Exception in thread "main" scala.MatchError: (Filters(List(BNG),List(school)),List()) (of class scala.Tuple2)

我不知道我在做什么错? 我有三种输入类型:

BNG:school
HYD:school,restaurant
BNG:school | HYD:school,restaurant

代码适用于这些类型的输入。

其次: 如果我从filter1和filter2删除数据类型Filters 它给出的输出为:

filter1: Filters = Filters(List(BNG),List(school))
filter2: Product with Serializable = Filters(List(HYD),restaurant))

为什么 filter1 Filters 类型,而 filter2 具有可序列化的产品类型?

感谢您的帮助。

谢谢。

解决方法

您的代码假定input包含2个Filter字符串。如果它实际上包含少于2个或大于2个,那么事情就会崩溃。

这可以通过以多种方式多次拆分输入来完成。

val input = "BNG:school | HYD:school,restaurant|LND,HKG:restaurant"
input.split("\\s*\\|\\s*")
  .map { ss =>
    val Array(cs,ts) = ss.split("\\s*:\\s*")
    Filters(cs.split(",").toList,ts.split(",").toList)
  }.toList

//res0 = List(Filters(List(BNG),List(school))
//,Filters(List(HYD),List(school,restaurant))
//,Filters(List(LND,HKG),List(restaurant)))

或者您可以使用正则表达式模式来帮助隔离数据元素。

val re = "([^|:]+):([^|:]+)".r
re.findAllMatchIn(input)
  .map(m => Filters(m.group(1).trim.split(",m.group(2).trim.split(",").toList)).toList

此处的一大优势是,正则表达式搜索将仅跳过格式不正确的数据(例如丢失的:),而不会引发错误。


正则表达式模式说明:

  • [^|:]-不是^表示 not )的任何字符,竖杠|或冒号{{1 }}
  • :-必须至少是其中的1个([^|:]+表示 1或更多
  • +-记住该组(父母创建了一个“捕获组”)
  • ([^|:]+)-2个捕获组(([^|:]+):([^|:]+)group(1))之间用冒号group(2)隔开。每个组均由冒号前后的所有字符组成,这些字符不是长条或冒号字符。
,

正如评论者所说,这是因为您在等式的左侧有一个元组。

也许以下几点更好?

case class Filters(city: List[String],isType: List[String])

val regex = "(\\w+):([,\\w]+)".r
val input = "BNG:school | HYD:school,restaurant"

val result = input.split("\\|").map(regex.findFirstMatchIn).map {
  case Some(m) => Filters(List(m.group(1)),m.group(2).split(",").toList)
  case None => throw new Exception("No match")
}

println(result) // Array(Filters(List("BNG"),List("school")),Filters(List("HYD"),List("school","restaurant")))

在匹配块中使用if-guards时,也要注意2.12上的https://github.com/scala/bug/issues/5365