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

别等时光非礼了梦想. 提交于 2019-12-06 09:21:11

You don't have to match on Nil. What you could do instead is match on the rest.

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

With this code rest is than either a List or Nil and you make sure that the element has more than 1 element with the match on ts and then rest

By rearranging the cases and adding a third case for completeness (exhaustive matching), this captures the intended semantics,

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

Note the second case extracts the first element and the rest which cannot be an empty list (Nil) since this possibility did not match in the first case: here, xs includes at least one more non empty list; the last case covers empty lists.

In addition to the other answers, you can also use the apply method of the companion List object like so:

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

To generalize this problem, you might write your own extractor. To match on lists of an arbitrary length with a given first element, you could:

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

And then you can match:

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

ts is matching one element of the list. In your first case, ts matches 2 i.e. 1 in the list matches with 1 in the pattern statement, ts matches with 2 in the list and Nil in the list matches with Nil in the pattern statement and you don't get any 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

In your second case, you are trying to match ts with both 2 and 3 which is not possible and hence it throws a MatchError.

If you want to do a pattern match based on size, you can do

 
 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

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!