Scala pattern matching for non-list sequences

I have the following code that recursively works with every item in a List

def doMatch(list: List[Int]): Unit = list match { case last :: Nil => println("Final element.") case head :: tail => println("Recursing..."); doMatch(tail) } 

Now, ignoring that this function is available through filter () and foreach (), this works just fine. However, if I try to change it to accept any Seq [Int], I run into problems:

  • Seq does not have ::, but it has + :, which, as I understand it, is basically the same. If I try to match +: tail on the head, however, the compiler complains about the error: not found: value +: '
  • Nil is specific to List, and I'm not sure what to replace it with. I am going to try Seq () if I ever get past the previous problem.

This is how I think the code should look, except that it does not work:

 def doMatch(seq: Seq[Int]): Unit = seq match { case last +: Seq() => println("Final element.") case head +: tail => println("Recursing..."); doMatch(tail) } 

Edit: so many good answers! I accept agilesteel's answer as it was the first to note that :: is not an operator in my example, but a case class and therefore a difference.

+42
collections scala pattern-matching
Jul 24 2018-11-21T00:
source share
6 answers

There are two :: in Scala :: (pronounced cons). One of them is the operator defined in the class List , and one is the class (a subclass of List ), which is a non-empty list characterized by a head and a tail.

head :: tail is a constructor template that is syntactically modified from ::(head, tail) .

:: is the case class, which means that an extractor object is defined for it.

+20
Jul 24 '11 at 15:07
source share

Kind of cheating, but here it goes:

 def doMatch(seq: Seq[Int]): Unit = seq match { case Seq(x) => println("Final element " + x) case Seq(x, xs@_*) => println("Recursing..." + x); doMatch(xs) } 

Do not ask me why xs* does not work ...

+46
Jul 24 '11 at 16:24
source share

Starting in March 2012, this works at 2.10 +:

  def doMatch(seq: Seq[Int]): Unit = seq match { case last +: Seq() => println("Final element.") case head +: tail => println("Recursing..."); doMatch(tail) } //> doMatch: (seq: Seq[Int])Unit doMatch(List(1, 2)) //> Recursing... //| Final element. 

In general, for Seq , two different head / tail and init / last decposition objects have been added to SeqExtractors, reflecting append / prepend:

 List(1, 2) match { case init :+ last => last } //> res0: Int = 2 List(1, 2) match { case head +: tail => tail } //> res1: List[Int] = List(2) Vector(1, 2) match { case init :+ last => last } //> res2: Int = 2 Vector(1, 2) match { case head +: tail => tail } //> res3: scala.collection.immutable.Vector[Int] = Vector(2) 
+36
02 Oct '13 at
source share

In fact, you can define an object for +: to accomplish exactly what you are looking for:

 object +: { def unapply[T](s: Seq[T]) = if(s.nonEmpty) Some(s.head, s.tail) else None } scala> val h +: t = Seq(1,2,3) h: Int = 1 t: Seq[Int] = List(2, 3) 

Then your code works exactly as expected.

This works because h +: t equivalent to +:(h,t) when used for matching patten.

+22
Jul 24 '11 at 17:12
source share

I do not think that there is support for pattern matching for arbitrary sequences in the standard library. You could do this with no pattern matching:

  def doMatch(seq: Seq[Int]) { if (seq.size == 1) println("final element " + seq(0)) else { println("recursing") doMatch(seq.tail) } } doMatch(1 to 10) 

However, you can define your own extractor objects. See http://www.scala-lang.org/node/112

 object SEQ { def unapply[A](s:Seq[A]):Option[(A, Seq[A])] = { if (s.size == 0) None else { Some((s.head, s.tail)) } } } def doMatch(seq: Seq[Int]) { seq match { case SEQ(head, Seq()) => println("final") case SEQ(head, tail) => { println("recursing") doMatch(tail) } } } 
+4
Jul 24 2018-11-11T00:
source share

A simple transformation from Seq to List will do the following:

 def doMatch (list: List[Int]): Unit = list match { case last :: Nil => println ("Final element.") case head :: tail => println ("Recursing..."); doMatch (tail) case Nil => println ("only seen for empty lists") } def doMatchSeq (seq: Seq[Int]) : Unit = doMatch (seq.toList) doMatch (List(3, 4, 5)) doMatchSeq (3 to 5) 
-one
Jul 25 '11 at 14:41
source share



All Articles