Reputation: 2710
I'm looking to match a sequence within a sequence like either in ex 1 or ex 2;
List(1, 2, 3, 4) match {
case 1 :: List(_*) :: 4 :: tail => // Ex 1
case 1 :: (seq : List[Int]) :: 4 :: tail => // Ex 2
case _ =>
}
This is a variant to the fixed length sequence pattern _*
. Like the _*
, I don't care about the content of the inner pattern, but it is important that the length can vary and that the pattern is surrounded by a prefix (such as the 1
above) and a suffix (like the 4
).
My question is if any of you have a trick to do this by some crafty unapply
magic or if you'd just iterate the list to search for the sequence manually.
Thanks in advance! :-)
Upvotes: 0
Views: 384
Reputation: 35
You could do something like this:
List(1,2,3,4) match {
case 1 :: tail if tail.last == 4 => println("Found it!")
}
But if you check the implementation of last
in LinearSeqOptimized
:
def last: A = {
if (isEmpty) throw new NoSuchElementException
var these = this
var nx = these.tail
while (!nx.isEmpty) {
these = nx
nx = nx.tail
}
these.head
}
It is iterating indeed. This is because List
inherits from LinearSeq
which are optimized to provide efficient head
and tail
operations. For what you want to do, it is better to use an IndexedSeq
implementation like Vector
which has an optimized length
operation, used in last
:
override /*TraversableLike*/ def last: A = {
if (isEmpty) throw new UnsupportedOperationException("empty.last")
apply(length-1)
}
So you could do something like this:
Vector(1,2,3,4) match {
case v if v.head == 1 && v.last == 4 => println("Found it!")
}
Upvotes: 1
Reputation: 167871
This is really outside the scope of what pattern matching is supposed to be used for. Even if you could finagle a set of custom unapply
methods to do what you wanted, it wouldn't be apparent whether matching was greedy or not, etc..
However, if you really want to, you can proceed as follows (for example):
import scala.collection.SeqLike
class Decon[A](a0: A, a1: A) {
def unapply[C <: SeqLike[A, C]](xs: C with SeqLike[A, C]): Option[(C, C)] = {
xs.span(_ != a1) match {
case (a0 +: pre, a1 +: post) => Some((pre,post))
case _ => None
}
}
}
val Dc = new Decon(1,4)
scala> List(1,2,3,4,5) match { case Dc(pre, post) => (pre, post); case _ => (Nil, Nil) }
res1: (List[Int], List[Int]) = (List(2, 3),List(5))
Separating the specification of the fixed elements 1
and 4
from the match is necessary; otherwise the normal algorithm would ask the unapply
to return values without any knowledge of what was being sought, and then would test to make sure they were correct.
Upvotes: 2