0

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

4 Answers 4

1

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

Sign up to request clarification or add additional context in comments.

4 Comments

Ok, so the short answer to my question is 'not possible' .... I am quite disappointed honestly but at least it is clear.
@sam Yes. My point is: this is not possible syntactically, but lot's of things are possible semantically by using custom matchers.
I am simply surprised that it compiles then. Is it failing at runtime but hidden by the pattern matching process ?
@sam Why this is compiles - is another question. I'll try to clarify that.
0

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. 

1 Comment

if index of "to" is 1 and "from" does not exist, it gives a false positive.
0

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 } 

7 Comments

it is in fact a Seq of objects, sorry for not saying it, I edited my post
Even as a Seq[Object] works. Perhaps you could add an example of the sequence.
I don't see how it could work since :: is only usable on List
If you check :: in this link you can see all the types that can use :: with.
I think I understand what you mean. But :: is more than a List operator. I have found this post about it.
|
0

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 } 

1 Comment

unfortunately it doesn't work, probably because there could be any number of elements before from

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.