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

这一生的挚爱 提交于 2020-01-23 16:19:27

问题


My codes looks like this:

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

I tried to use 1::ts::Nil to match the List who starts with 1 and whose length is greater than 1. It workes well for 2-element list, however, this pattern doesn't work for 3-element list, for example:

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"
}

This won't work..Does anyone have ideas about this?


回答1:


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




回答2:


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.




回答3:


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"
}



回答4:


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") 
}



回答5:


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



来源:https://stackoverflow.com/questions/28644861/in-scala-how-can-i-use-pattern-match-to-match-a-list-with-specified-length

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