Reputation: 3511
I would like to look for a specific pattern inside a Seq. I tried to use at the same time :+ and +: operators but it doesn't seem to work even though it compiles, for now I have to rely on 'dropwhile' operation first and then pattern match on the beginning of the collection.
Is it not possible to write something like that in Scala ? 'from' and 'to' are existing variables
case beginColl :+ `from` +: someElement +: `to` +: tail => true
Edit : it is a Seq of objects , not a list
Upvotes: 0
Views: 74
Reputation: 16328
This will never work in that definition as you can not wildcard any subsequence except tail in result of unapplySeq
.
But let me suggest a workaround.
Lets define this helper:
object Span {
class Spanner[T](pred: T => Boolean) {
def unapply(seq: Seq[T]) = for {
idx <- Some(seq.indexWhere(pred)) if idx >= 0
(start, elem +: end) = seq.splitAt(idx)
} yield (start, end)
}
def apply[T](pred: T => Boolean) = new Spanner(pred)
}
This allows us to define more interesting matchers like this function:
def splitter[T](from:T, to:T): PartialFunction[Seq[T], Seq[Seq[T]]] = {
val From = Span[T](_ == from)
val To = Span[T](_ == to)
{
case From(prefix, To(middle, postfix)) => Seq(prefix, middle, postfix)
}
}
So if we specialize it to:
val mySplitter = splitter("from", "to").lift
We could get appropriate results:
mySplitter(Seq("1", "2", "to", "3", "4", "from", "5", "6")) // None
mySplitter(Seq("1", "2", "from", "3", "4", "to", "5", "6")) // Some(List(List(1, 2), List(3, 4), List(5, 6)))
Lets try to clarify how compiler understands your syntax, lets define
def splitter2(from: AnyRef, to: AnyRef): PartialFunction[Seq[_], AnyRef] = {
case beginColl :+ `from` +: someElement +: `to` +: tail => (beginColl, someElement, tail)
}
val mySplitter2 = splitter2("from", "to").lift
So if we try to match
mySplitter2(Seq("1", "2", "from", "3", "4 ", "to", "5", "6"))
We'll surely get None
But if we try
mySplitter2(Seq("1", "2", Seq("from", "3", "to", "4", "5")))
Suddenly we getting Some(...)
So compiler just understood your expression as _match element as
beginColl :+ __last
then match __last
as
`from` +: someElement +: `to` +: tail
Which is basically verify this is non-empty Seq
last element of which is
another Seq
that consists of at least three elements, first and third of these are from
and to
Upvotes: 1
Reputation: 8851
This should work:
case Seq(_, `from`, _, `to`, _*) => true
EDIT:
if there are more elements before 'from', dropWhile is a good solution, an alternative (but less efficient) way could be :
def matchSeq[T](s: Seq[T]): Boolean = s match {
case Seq(_, `from`, _, `to`, _*) => true
case Seq(a, b@_*) => matchSeq(b)
case _ => false
}
Upvotes: 0
Reputation: 2804
I thought that you may need to recognize a sequence like this:
val test = Seq("x", "from", "y", "to", "z")
test match {
case _ :: "from" :: _ :: "to" :: _ => true
case _ => false
}
But as you need to know if the sequence has a particular characteristic, I would try this:
test match {
case list if (list.indexOf("from") < list.indexOf("to")) => true
case _ => false
}
Upvotes: 0
Reputation: 1288
You can use containsSlice
to check the sequence contains subsequence, or you can compare indexes of elements you are looking for. i.e:
val test = Seq("x", "from", "y", "to", "z")
val test2 = Seq("u", "w", "x", "from", "y", "to", "z")
test match {
case s if s.indexOf("to") - s.indexOf("from") == 2 => true
case _ => false
} //true in both cases.
Upvotes: 0