在 Scala 中,如何使用模式匹配来匹配指定长度的列表?

In scala, how can I use pattern match to match a list with specified length?

我的代码如下所示:

1::2::Nil match {
  case 1::ts::Nil => "Starts with 1. More than one element"
  case 1::Nil => "Starts with 1. Only one element"
}

我尝试使用1::ts::Nil来匹配以1开头且长度大于1的列表。它适用于2元素列表,但是,这种模式并不适用为 3-element list 工作,例如:

1::2::3::Nil match {
  case 1::ts::Nil => "Starts with 1. More than one element"
  case 1::Nil => "Starts with 1. Only one element"
}

这行不通..有人对此有想法吗?

您不必匹配 Nil。你可以做的是在其余部分进行匹配。

1::Nil match {
   case 1::ts::rest => "Starts with 1. More than one element"
   case 1::Nil => "Starts with 1. Only one element"
}

使用此代码,rest 不是 List 或 Nil,并且您确保该元素有超过 1 个元素与 ts 匹配,然后是 rest

ts 匹配列表中的一个元素。在您的第一种情况下,ts 匹配 2,即列表中的 1 与模式语句中的 1 匹配,ts 与列表中的 2 匹配,并且 [列表中的 =16=] 与模式语句中的 Nil 匹配,您不会得到任何 MatchError


1 :: 2 :: Nil match {
    case 1 :: ts :: Nil => println("ts is "+ts)
                         "Starts with 1. More than one element"
    case 1 :: Nil       => "Starts with 1. Only one element"
  } //> ts is 2
   //| res1: String = Starts with 1. More than one element</pre>

在你的第二种情况下,你试图将 ts 与 2 和 3 进行匹配,这是不可能的,因此它会抛出 MatchError。

如果你想根据大小进行模式匹配,你可以这样做

 
 1 :: 2 :: 3 :: Nil match {
    case 1 :: tail if tail.size > 1 => "Starts with 1. More than one element"
    case 1 :: Nil                   => "Starts with 1. Only one element"
  }   //> res0: String = Starts with 1. More than one element</pre>

If case in the pattern match can be any condition for your real use case

通过重新排列案例并添加第三个案例以确保完整性(穷举匹配),这捕获了预期的语义,

1 :: 2 :: 3 :: Nil match {
  case 1 :: Nil => "Just one"
  case 1 :: xs => "One and more"
  case _ => "Unknown"
}

注意第二种情况提取第一个元素,其余元素不能为空列表 (Nil),因为这种可能性在第一种情况下不匹配:这里,xs 至少包括又一个非空列表;最后一个案例涵盖了空列表。

要概括这个问题,您可以编写自己的提取器。要将任意长度的列表与给定的第一个元素匹配,您可以:

object HeadWithLength {
    def unapply[A](list: Seq[A]): Option[(Option[A], Int)] = 
        Some(list.headOption -> list.length)
}

然后你可以匹配:

List(1, 3, 4) match {
    case HeadWithLength(Some(head), length) => println(s"Head is $head and length is $length") 
}

除了其他答案,您还可以像这样使用伴随列表对象的 apply 方法:

1::2::3::Nil match {
  case List(1, _, _*) => "Starts with 1. More than one element"
  case List(1) => "Starts with 1. Only one element"
}