Say I have the following:
trait PropType
case class PropTypeA(String value) extends PropType
case class PropTypeB(String value) extends PropType
case class Item(
propTypeA: PropTypeA,
propTypeB: PropTypeB
)
and that I'm given a List[PropType]. How would I go with combining this into a List[Item]?
That is (and assuming we only have PropTypeA(name: String) and PropTypeB(name: String) to make this shorter / easier to follow hopefully) given this:
List[PropType](
PropTypeA("item1-propTypeA"),
PropTypeB("item1-propTypeB"),
PropTypeA("item2-propTypeA"),
PropTypeB("item2-propTypeB")
]
I'd like to get the equivalent of:
List[Item](
Item(PropTypeA("item1-propTypeA"), PropTypeB("item1-propTypeB")),
Item(PropTypeA("item2-propTypeA"), PropTypeB("item2-propTypeB"))
)
Kind of table building from linearized rows across columns, if that makes sense.
Note that in general there might be incomplete "rows", e.g. this:
List[PropType](
PropTypeA("item1-propTypeA"),
PropTypeB("item1-propTypeB"),
PropTypeB("itemPartialXXX-propTypeB"),
PropTypeA("itemPartialYYY-propTypeA"),
PropTypeA("item2-propTypeA"),
PropTypeB("item2-propTypeB")
]
should generate the same output as the above, with the logic being that PropTypeA always marks the start of a new row and thus everything "unused" is discarded.
How should I approach this?
Something like this will work with examples you mentioned.
list.grouped(2).collect { case Seq(a: PropTypeA, b: PropTypeB) => Item(a,b) }.toList
However it is unclear from your question what other cases you want to handle and how. For example, how exactly do you define the "partial" occurrence. Are there always two elements in reverse order? Can there be just one, or three? Can there be two As in a row? Or three? Or two Bs?
For example, A, A, A, A, B or B, B, A, A, B or just A?
Depending on how you answer those question, you'll need to somehow "pre-filter" the list before hand.
Here is an implementation based on the last phrase in your question: "PropTypeA always marks the start of a new row and thus everything "unused" is discarded." It only looks for instances where an A is immediately followed by B and discards everything else:
list.foldLeft(List.empty[PropType]) {
case ((a: PropTypeA) :: tail, b: PropTypeB) => b :: a :: tail
case ((b: PropTypeB) :: tail, a: PropTypeA) => a :: b :: tail
case (Nil, a: PropTypeA) => a :: Nil
case (_ :: tail, a: PropTypeA) => a :: tail
case (list, _) => list
}.reverse.grouped(2).collect {
case Seq(a: PropTypeA, b: PropTypeB) => Item(a,b)
}.toList
If you have more than just two types, then there are even more questions: what happens if stuff after A comes in wrong order for example? Like what do you do with A,B,C,A,C,B?
But basically, it would be the same idea as above: if next element is of the type you expect in the the sequence, add it to the result, otherwise discard sequence and keep going.
we can use the tail recursion function to generate the list of a new type.
def transformType(proptypes: List[PropType]): List[Item] =
{
// tail recursion function defined
#tailrec
def transform(proptypes: List[PropType], items: List[Item]): List[Item]=
{
proptypes match {
case (first:PropTypeA) :: (second:PropTypeB) :: tail=> transform(tail, items :+ Item(first, second))
case (first:PropTypeA) :: (second:PropTypeA) :: tail => transform(second :: tail, items :+ Item(first, PropTypeB("")))
case (first:PropTypeB) :: tail => transform(tail, items :+ Item(PropTypeA(""), first))
case (first:PropTypeA) :: tail => transform(tail, items :+ Item(first, PropTypeB("")))
case _ => items
}
}
transform(proptypes, List.empty[Item])
}
you can find the working link here
Related
I've been provided with a solution on another question that works with List[Int] to get the indices at which two lists intersect.
#tailrec
def indicesOfIntersection(left: List[Int], right: List[Int], lidx: Int = 0, ridx: Int = 0, result: List[(Int, Int)] = Nil): List[(Int, Int)] = (left, right) match {
case (Nil, _) | (_, Nil) => result.reverse
case (l::tail, r::_) if l < r => indicesOfIntersection(tail, right, lidx+1, ridx, result)
case (l::_, r::tail) if l > r => indicesOfIntersection(left, tail, lidx, ridx+1, result)
case (l::ltail, r::rtail) => indicesOfIntersection(ltail, rtail, lidx+1, ridx+1, (lidx, ridx) :: result)
}
And am hoping to convert it to one that works directly on Arrays, without doing anything inefficient (because it is used on big arrays)
The advice I got was
Yeah Nil and head::tail extractors only work for lists. You can trivially rewrite it for any seq: just replace Nil with Seq() and head::tail with head+:tail
Unfortunately, the trivial is still over my head and I require a bit more spoon feeding :)
I took a stab with the following which passed compile-time checks, but fails at runtime:
#tailrec
def indicesOfIntersection(left: Seq[Int], right: Seq[Int], lidx: Int = 0, ridx: Int = 0, result: Seq[(Int, Int)] = Seq()): Seq[(Int, Int)] = (left, right) match {
case (l::tail, r::_) if l < r => indicesOfIntersection(tail, right, lidx+1, ridx, result)
case (l::_, r::tail) if l > r => indicesOfIntersection(left, tail, lidx, ridx+1, result)
case (l::ltail, r::rtail) => indicesOfIntersection(ltail, rtail, lidx+1, ridx+1, (lidx, ridx) +: result)
case (Seq(), _) | (_, Seq()) => result.reverse
}
scala.MatchError: (WrappedArray(19, 68, 419...
(error truncated due to essentially being a dump of my first array input)
Thanks in advance for pointing out what I've done wrong.
Update
I've gotten a little further by updating all instances of :: operators in the case statements to +: as well (although I don't understand the syntax, thinking this was just a place for destructuring).
It's running now, but I'm unsure if it's "correct" - specifically it seems that if the final case (formerly the first case) is comparing empty Seq() instances, they won't necessarily be considered equal just because the are both empty (unlike the former Nil comparison)?
Update
I've been informed in the comments that one cannot simply use tail on Arrays without incurring a heavy cost (presumably traversing the entire array?) so I'm still looking for a solution which replaces these nuances with something that is equally performant for Arrays (or any Seq, if you wish)
Try wrapping your Array using scala.collection.immutable.ArraySeq.unsafeWrapArray and then converting it to a View using .view.
Making it a View will allow you to take the tail without actually copying the underlying array.
See also https://docs.scala-lang.org/overviews/collections-2.13/views.html
Suppose I have a tree data structure like this:
trait Node { val name: String }
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node
Suppose also I've got a function to map over leaves:
def mapLeaves(root: Node, f: LeafNode => LeafNode): Node = root match {
case ln: LeafNode => f(ln)
case bn: BranchNode => BranchNode(bn.name, bn.children.map(ch => mapLeaves(ch, f)))
}
Now I am trying to make this function tail-recursive but having a hard time to figure out how to do it. I've read this answer but still don't know to make that binary tree solution work for a multiway tree.
How would you rewrite mapLeaves to make it tail-recursive?
"Call stack" and "recursion" are merely popular design patterns that later got incorporated into most programming languages (and thus became mostly "invisible"). There is nothing that prevents you from reimplementing both with heap data structures. So, here is "the obvious" 1960's TAOCP retro-style solution:
trait Node { val name: String }
case class BranchNode(name: String, children: List[Node]) extends Node
case class LeafNode(name: String) extends Node
def mapLeaves(root: Node, f: LeafNode => LeafNode): Node = {
case class Frame(name: String, mapped: List[Node], todos: List[Node])
#annotation.tailrec
def step(stack: List[Frame]): Node = stack match {
// "return / pop a stack-frame"
case Frame(name, done, Nil) :: tail => {
val ret = BranchNode(name, done.reverse)
tail match {
case Nil => ret
case Frame(tn, td, tt) :: more => {
step(Frame(tn, ret :: td, tt) :: more)
}
}
}
case Frame(name, done, x :: xs) :: tail => x match {
// "recursion base"
case l # LeafNode(_) => step(Frame(name, f(l) :: done, xs) :: tail)
// "recursive call"
case BranchNode(n, cs) => step(Frame(n, Nil, cs) :: Frame(name, done, xs) :: tail)
}
case Nil => throw new Error("shouldn't happen")
}
root match {
case l # LeafNode(_) => f(l)
case b # BranchNode(n, cs) => step(List(Frame(n, Nil, cs)))
}
}
The tail-recursive step function takes a reified stack with "stack frames". A "stack frame" stores the name of the branch node that is currently being processed, a list of child nodes that have already been processed, and the list of the remaining nodes that still must be processed later. This roughly corresponds to an actual stack frame of your recursive mapLeaves function.
With this data structure,
returning from recursive calls corresponds to deconstructing a Frame object, and either returning the final result, or at least making the stack one frame shorter.
recursive calls correspond to a step that prepends a Frame to the stack
base case (invoking f on leaves) does not create or remove any frames
Once one understands how the usually invisible stack frames are represented explicitly, the translation is straightforward and mostly mechanical.
Example:
val example = BranchNode("x", List(
BranchNode("y", List(
LeafNode("a"),
LeafNode("b")
)),
BranchNode("z", List(
LeafNode("c"),
BranchNode("v", List(
LeafNode("d"),
LeafNode("e")
))
))
))
println(mapLeaves(example, { case LeafNode(n) => LeafNode(n.toUpperCase) }))
Output (indented):
BranchNode(x,List(
BranchNode(y,List(
LeafNode(A),
LeafNode(B)
)),
BranchNode(z, List(
LeafNode(C),
BranchNode(v,List(
LeafNode(D),
LeafNode(E)
))
))
))
It might be easier to implement it using a technique called trampoline.
If you use it, you'd be able to use two functions calling itself doing mutual recursion (with tailrec, you are limited to one function). Similarly to tailrec this recursion will be transformed to plain loop.
Trampolines are implemented in scala standard library in scala.util.control.TailCalls.
import scala.util.control.TailCalls.{TailRec, done, tailcall}
def mapLeaves(root: Node, f: LeafNode => LeafNode): Node = {
//two inner functions doing mutual recursion
//iterates recursively over children of node
def iterate(nodes: List[Node]): TailRec[List[Node]] = {
nodes match {
case x :: xs => tailcall(deepMap(x)) //it calls with mutual recursion deepMap which maps over children of node
.flatMap(node => iterate(xs).map(node :: _)) //you can flat map over TailRec
case Nil => done(Nil)
}
}
//recursively visits all branches
def deepMap(node: Node): TailRec[Node] = {
node match {
case ln: LeafNode => done(f(ln))
case bn: BranchNode => tailcall(iterate(bn.children))
.map(BranchNode(bn.name, _)) //calls mutually iterate
}
}
deepMap(root).result //unwrap result to plain node
}
Instead of TailCalls you could also use Eval from Cats or Trampoline from scalaz.
With that implementation function worked without problems:
def build(counter: Int): Node = {
if (counter > 0) {
BranchNode("branch", List(build(counter-1)))
} else {
LeafNode("leaf")
}
}
val root = build(4000)
mapLeaves(root, x => x.copy(name = x.name.reverse)) // no problems
When I ran that example with your implementation it caused java.lang.StackOverflowError as expected.
I'd like to be able to use a single variable multiple times within one pattern, so that it will only match if the same value is present in all places, such as
list match {
case x :: x :: xs => // recurse
}
which would match List(1,1,2) but would not match List(1,2,1). But this does not compile with error: x is already defined as value x.
In researching this question, I found out that I can also include a guard in the case clause, so I can do
list match {
case x1 :: x2 :: xs if x1==x2 => // recurse
}
which seems to work the same way (it does, right?). This is good, but it wouldn't look as clean if I wanted the same value in many places, like
list match {
case x1::x2::x3::x4::xs if x1==x2 && x2==x3 && x3==x4 => // recurse
}
Is there any more elegant way I can do this?
A few notes: Yes, I am just learning scala, if that wasn't clear, so I'm not sure this is something I'd ever really want to do, but I'm just interested in what's possible. In that regard, I'm not really looking for a completely different solution, like takeWhile or filter or something, but more so am specifically interested in pattern matching.
Scala doesn't provide quite that much flexibility with its matches (which may be a good thing, as one has to be aware of errors arising from unintentional variable re-use).
If you have a large number of identical items, you might want to consider a nested match (but note that you won't fail out of the inner match to be completed later down the outer match, so you have to handle everything locally):
list match {
case x :: rest => rest match {
case `x` :: `x` :: `x` :: xs => println("Four of the same")
case _ => println("Well, nonempty at least")
}
case _ => println("Boring, there's nothing here!")
}
Note the backticks which mean "we've already got this variable, check against it, don't set it!".
Alternatively, if you have specialized functionality that you use repeatedly, you can create a custom matcher:
object FourOf {
def unapplySeq(xs: List[Int]): Option[(Int, List[Int])] = xs match {
case x :: y :: z :: a :: rest if x==y && y==z && z==a => Some((x,rest))
case _ => None
}
}
and then use it whenever you need that complicated pattern:
list match {
case FourOf(x,rest) => println("four of the same")
case x :: more => println("Nonempty")
case _ => println("Yawn")
}
Neither of these are quite as tidy and flexible as what you were apparently hoping for, but then again, I'm not sure flipping between assigning and testing the same variable in a match statement is a good way to write clear code anyway.
For many repeats you might use stable identifiers to do a comparison (instead of catching a value):
val x = list.head
list match {
case `x`::`x`::`x`::`x`::xs => ....
}
But note that this won't work on empty list (you just cannot get head of it).
I think Rex's answer rocks. I am a fan of unapplySeq. But here's a not-so-clever-and-maybe-wasteful alternative, if your main bother is just with the sequence of =='s in each guard.
So in the TMTOWTDI spirit:
def same[A](xs: A*) = xs forall (xs.head==)
// Then in your pattern match,
list match {
// case x1::x2::x3::x4::xs if x1==x2 && x2==x3 && x3==x4 => // recurse
case x1::x2::x3::x4::xs if same(x1,x2,x3,x4) => // recurse
}
I like Om's answer as well, so here's an adaptation:
list.headOption map (x => list match {
case `x`::`x`::`x`::`x`::xs => //...;
case _ => // ...
}) getOrElse {
// do what you'd have done for an empty list...
}
Trying to get a handle on pattern matching here-- coming from a C++/Java background it's very foreign to me.
The point of this branch is to check each member of a List d of tuples [format of (string,object). I want to define three cases.
1) If the counter in this function is larger than the size of the list (defined in another called acc), I want to return nothing (because there is no match)
2) If the key given in the input matches a tuple in the list, I want to return its value (or, whatever is stored in the tuple._2).
3) If there is no match, and there is still more list to iterate, increment and continue.
My code is below:
def get(key:String):Option[Any] = {
var counter: Int = 0
val flag: Boolean = false
x match {
case (counter > acc) => None
case ((d(counter)._1) == key) => d(counter)._2
case _ => counter += 1
}
My issue here is while the first case seems to compile correctly, the second throws an error:
:36: error: ')' expected but '.' found.
case ((d(counter)._1) == key) => d(counter)._2
The third as well:
scala> case _ => counter += 1
:1: error: illegal start of definition
But I assume it's because the second isn't correct. My first thought is that I'm not comparing tuples correctly, but I seem to be following the syntax for indexing into a tuple, so I'm stumped. Can anyone steer me in the right direction?
Hopefully a few things to clear up your confusion:
Matching in scala follows this general template:
x match {
case SomethingThatXIs if(SomeCondition) => SomeExpression
// rinse and repeat
// note that `if(SomeCondition)` is optional
}
It looks like you may have attempted to use the match/case expression as more of an if/else if/else kind of block, and as far as I can tell, the x doesn't really matter within said block. If that's the case, you might be fine with something like
case _ if (d(counter)._1 == key) => d(counter)._2
BUT
Some info on Lists in scala. You should always think of it like a LinkedList, where indexed lookup is an O(n) operation. Lists can be matched with a head :: tail format, and Nil is an empty list. For example:
val myList = List(1,2,3,4)
myList match {
case first :: theRest =>
// first is 1, theRest is List(2,3,4), which you can also express as
// 2 :: 3 :: 4 :: Nil
case Nil =>
// an empty list case
}
It looks like you're constructing a kind of ListMap, so I'll write up a more "functional"/"recursive" way of implementing your get method.
I'll assume that d is the backing list, of type List[(String, Any)]
def get(key: String): Option[Any] = {
def recurse(key: String, list: List[(String, Any)]): Option[Any] = list match {
case (k, value) :: _ if (key == k) => Some(value)
case _ :: theRest => recurse(key, theRest)
case Nil => None
}
recurse(key, d)
}
The three case statements can be explained as follows:
1) The first element in list is a tuple of (k, value). The rest of the list is matched to the _ because we don't care about it in this case. The condition asks if k is equal to the key we are looking for. In this case, we want to return the value from the tuple.
2) Since the first element didn't have the right key, we want to recurse. We don't care about the first element, but we want the rest of the list so that we can recurse with it.
3) case Nil means there's nothing in the list, which should mark "failure" and the end of the recursion. In this case we return None. Consider this the same as your counter > acc condition from your question.
Please don't hesitate to ask for further explanation; and if I've accidentally made a mistake (won't compile, etc), point it out and I will fix it.
I'm assuming that conditionally extracting part of a tuple from a list of tuples is the important part of your question, excuse me if I'm wrong.
First an initial point, in Scala we normally would use AnyRef instead of Object or, if worthwhile, we would use a type parameter which can increase reuse of the function or method and increase type safety.
The three cases you describe can be collapsed into two cases, the first case uses a guard (the if statement after the pattern match), the second case matches the entire non-empty list and searches for a match between each first tuple argument and the key, returning a Some[T] containing the second tuple argument of the matching tuple or None if no match occurred. The third case is not required as the find operation traverses (iterates over) the list.
The map operation after the find is used to extract the second tuple argument (map on an Option returns an Option), remove this operation and change the method's return type to Option[(String, T)] if you want the whole tuple returned.
def f[T](key: String, xs: List[(String, T)], initialCount: Int = 2): Option[T] = {
var counter = initialCount
xs match {
case l: List[(String, T)] if l.size < counter => None
case l: List[(String, T)] => l find {_._1 == key} map {_._2}
}
}
f("A", List(("A", 1), ("B", 2))) // Returns Some(1)
f("B", List(("A", 1), ("B", 2))) // Returns Some(2)
f("A", List(("A", 1))) // Returns None
f("C", List(("A", 1), ("B", 2))) // Returns None
f("C", Nil) // Returns None
First, why are you using a List for that reason? What you need is definitely a Map. Its get() returns None if key is not found and Some(value) if it is found in it.
Second, what is x in your example? Is it the list?
Third, you cannot write case (log) => .. where log is a logical condition, it is in the form of case _ if (log) => ... (as Rex Kerr already pinted out in his comment).
Fouth, you need a recursive function for this (simply increasing the counter will call this only on the second element).
So you'll need something like this (if still prefer sticking to List):
def get(l: List[Tuple2[String, String]], key: String): Option[String] = {
if (l.isEmpty) {
None
} else {
val act = l.head
act match {
case x if (act._1 == key) => Some(act._2)
case _ => get(l.tail, key)
}
}
}
I've obviously done a very poor job of explaining what I'm looking for in my original post so let's try this one more time. What I'm trying to accomplish is the ability to pass a sequence of items, extract one or more of the items, and then pass the REMAINDER of the sequence on to another extractor. Note that by sequence I mean sequence (not necessarily a List). My previous examples used list as the sequence and I gave some examples of extraction using cons (::), but I could just as well pass an Array as my sequence.
I thought I knew how pattern matching and extraction worked but I could be wrong so to avoid any more basic comments and links to how to do pattern matching sites here's my understanding:
If I want to return a single item from my extractor I would define an unapply method. This method takes whatever type I chose as input (the type could be a sequence...) and returns a single optional item (the return type could itself be a sequence). The return must be wrapped in Some if I want a match or None if I don't. Here is an example that takes a sequence as input and returns the same sequence wrapped in Some but only if it contains all Strings. I could very well just return the sequence wrapped in Some and not do anything else, but this seems to cause confusion for people. The key is if it is wrapped in Some then it will match and if it is None it will not. Just to be more clear, the match will also not happen unless the input also matches my unapply methods input type. Here is my example:
object Test {
// In my original post I just returned the Seq itself just to verify I
// had matched but many people commented they didn't understand what I
// was trying to do so I've made it a bit more complicated (e.g. match
// only if the sequence is a sequence of Strings). Hopefully I don't
// screw this up and introduce a bug :)
def unapply[A](xs: Seq[A]): Option[Seq[String]] =
if (xs forall { _.isInstanceOf[String] })
Some(xs.asInstanceOf[Seq[String]])
else
None
}
Using List as an example, I can now perform the following:
// This works
def test1(xs: List[_]) = xs match {
case (s: String) :: Test(rest) =>
println("s = " + s + ", rest = " + rest)
case _ =>
println("no match")
}
test1(List("foo", "bar", "baz")) // "s = foo, rest = List(bar, baz)"
My test1 function takes List as input and extracts the head and tail using cons via the constructor pattern (e.g. ::(s, rest)). It then uses type ascription (: String) to make sure the head (s) is a String. The tail contains List("bar", "baz"). This is a List which means it is also a Seq (sequence). It is then passed as input to my Test extractor which verifies that both "bar" and "baz" are strings and returns the List wrapped in Some. Since Some is returned it is considered a match (although in my original post where I inadvertently mixed up unapplySeq with unapply this didn't work as expected, but that aside...). This is NOT what I'm looking for. This was only an example to show that Test does in fact extract a Seq as input as expected.
Now, here's where I caused mass confusion last time when I inadvertently used unapplySeq instead of unapply in my write up. After much confusion trying to understand the comments that were posted I finally picked up on the mistake. Many thanks to Dan for pointing me in the right direction...
But just be avoid any more confusion, let me clarify my understanding of unapplySeq. Like unapply, unapplySeq takes in whatever argument I choose as input, but instead of returning a single element it returns a sequence of elements. Each item in this sequence can then be used for additional pattern matching. Again, to make a match happen the input type must match and my returned sequence must be wrapped in Some and not be None. When extracting over the sequence of items returned from unapplySeq, you can use _* to match any remaining items not yet matched.
Ok, so my extractor takes a sequence as input and returns a sequence (as a single item) in return. Since I only want to return a single item as a match I need to use unapply NOT unapplySeq. Even though in my case I'm returning a Seq, I don't want unapplySeq because I don't want to do more pattern matching on the items in the Seq. I just want to return the items as a Seq on its own to then be passed to the body of my case match. This sounds confusing, but to those that understand unapply vs unapplySeq I hope it isn't.
So here is what I WANT to do. I want to take something that returns a sequence (e.g. List or Array) and I want to extract a few items from this sequence and then extract the REMAINDER of the items (e.g. _*) as a sequence. Let's call it the remainder sequence. I want to then pass the remainder sequence as input to my extractor. My extractor will then return the remaining items as a single Seq if it matches my criteria. Just to be 100% clear. The List (or Array, etc) will have its unapplySeq extractor called to create the sequence of items. I will extract a one or more of these items and then pass what is left as a sequence to my Test extractor which will use unapply (NOT unapplySeq) to return the remainder. If you are confused by this, then please don't comment...
Here are my tests:
// Doesn't compile. Is there a syntax for this?
def test2(xs: Seq[_]) = xs match {
// Variations tried:
// Test(rest) # _* - doesn't compile (this one seems reasonable to me)
// Test(rest # _*) - doesn't compile (would compile if Test had
// unapplySeq, but in that case would bind List's
// second element to Test as a Seq and then bind
// rest to that Seq (if all strings) - not what I'm
// looking for...). I though that this might work
// since Scala knows Test has no unapplySeq only
// unapply so # _* can be tied to the List not Test
// rest # Test(_*) - doesn't compile (didn't expect to)
case List(s: String, Test(rest) # _*) =>
println("s = " + s + " rest = " + rest)
case _ =>
println("no match")
}
// This works, but messy
def test3(xs: List[_]) = xs match {
case List(s: String, rest # _*) if (
rest match { case Test(rest) => true; case _ => false }
) =>
println("s = " + s + " rest = " + rest)
case _ =>
println("no match")
}
I created test3 based on comments from Julian (thanks Julian..). Some have commented that test3 does what I want so they are confused what I'm looking for. Yes, it accomplishes what I want to accomplish, but I'm not satisfied with it. Daniel's example also works (thanks Daniel), but I'm also not satisfied with having to create another extractor to split things and then do embedded extractions. These solutions seem too much work in order to accomplish something that seems fairly straight forward to me. What I WANT is to make test2 work or know that it can't be done this way. Is the error given because the syntax is wrong? I know that rest # _* will return a Seq, that can be verified here:
def test4(xs: List[_]) = xs match {
case List(s: String, rest # _*) =>
println(rest.getClass) // scala.collection.immutable.$colon$colon
case _ =>
println("no match")
}
It returns cons (::) which is a List which is a Seq. So how can I pass the _* Seq on to my extractor and have is return bound to the variable rest?
Note that I've also tried passing varargs to my unapply constructor (e.g. unapply(xs: A*)...) but that won't match either.
So, I hope it is clear now when I say I want to extract the remainder of a sequence in pattern matching. I'm not sure how else I can word it.
Based on the great feedback from Daniel I'm hoping he is going to have an answer for me :)
I'd like to extract the first item and pass the remainder on to another extractor.
OK. Your test1 does that, exactly. first_item :: Extractor(the_rest). The weird behavior you're seeing comes from your Test extractor. As you already had the answer to your stated question, and as expected behavior from your Test strikes you as a problem with test1, it seems that what you really want is some help with extractors.
So, please read Extractor Objects, from docs.scala-lang.org, and Pattern Matching in Scala (pdf). Although that PDF has an example of unapplySeq, and suggests where you'd want to use it, here are some extra examples:
object Sorted {
def unapply(xs: Seq[Int]) =
if (xs == xs.sortWith(_ < _)) Some(xs) else None
}
object SortedSeq {
def unapplySeq(xs: Seq[Int]) =
if (xs == xs.sortWith(_ < _)) Some(xs) else None
}
Interactively:
scala> List(1,2,3,4) match { case Sorted(xs) => Some(xs); case _ => None }
res0: Option[Seq[Int]] = Some(List(1, 2, 3, 4))
scala> List(4,1,2,3) match { case Sorted(xs) => Some(xs); case _ => None }
res1: Option[Seq[Int]] = None
scala> List(4,1,2,3) match { case first :: Sorted(rest) => Some(first, rest); case _ => None }
res2: Option[(Int, Seq[Int])] = Some((4,List(1, 2, 3)))
scala> List(1,2,3,4) match { case SortedSeq(a,b,c,d) => (a,b,c,d) }
res3: (Int, Int, Int, Int) = (1,2,3,4)
scala> List(4,1,2,3) match { case _ :: SortedSeq(a, b, _*) => (a,b) }
res4: (Int, Int) = (1,2)
scala> List(1,2,3,4) match { case SortedSeq(a, rest # _*) => (a, rest) }
res5: (Int, Seq[Int]) = (1,List(2, 3, 4))
Or maybe -- I only have the faint suspicion of this, you haven't said as much -- you don't want extractor help, but actually you want a terse way to express something like
scala> List(1,2,3,4) match { case 1 :: xs if (xs match { case Sorted(_) => true; case _ => false }) => xs }
res6: List[Int] = List(2, 3, 4)
Erlang has a feature like this (although, without these crazy extractors):
example(L=[1|_]) -> examine(L).
, which pattern-matches the same argument twice - to L and also to [1|_]. In Erlang both sides of the = are full-fledged patterns and could be anything, and you can add a third or more patterns with more =. Scala seems to only support the L=[1|_] form, having a variable and then a full pattern.
scala> List(4,1,2,3) match { case xs # _ :: Sorted(_) => xs }
collection.immutable.::[Int] = List(4, 1, 2, 3)
Well, the easiest way is this:
case (s: String) :: Test(rest # _*) =>
If you need this to work on general Seq, you can just define an extractor to split head from tail:
object Split {
def unapply[T](xs: Seq[T]): Option[(T, Seq[T])] = if (xs.nonEmpty) Some(xs.head -> xs.tail) else None
}
And then use it like
case Split(s: String, Test(rest # _*)) =>
Also note that if you had defined unapply instead of unapplySeq, then # _* would not be required on the pattern matched by Test.
:: is an extractor. For how it works (from a random googling), see, for example, here.
def test1(xs: List[_]) = xs match {
case s :: rest =>
println("s = " + s + " rest = " + rest)
case _ =>
println("no match")
}
scala> test1(List("a", "b", "c"))
s = a rest = List(b, c)
I think this is what you wanted?
Messing around with this, it seems that the issue has something to do with unapplySeq.
object Test {
def unapply[A](xs: List[A]): Option[List[A]] = Some(xs)
}
def test1(xs: List[_]) = xs match {
case (s: String) :: Test(s2 :: rest) =>
println("s = " + s + " rest = " + rest)
case _ =>
println("no match")
}
test1(List("foo", "bar", "baz"))
produces the output:
s = foo rest = List(baz)
I'm havng trouble googling up docs on the difference between unapply and unapplySeq.