- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
matching on tuples
Wed, 2010-03-31, 14:43
Is there a way to match on a tuple of unknown length? I'd like to do something like this, but of course, this doesn't compile. I'd like the third case to match if the tuple has more than 3 elements. Is that possible?
scala> def matcher(x:AnyRef) = x match {
| case (x,y) => println("found 2")
| case (x,y,z) => println("found 3")
| case (x,y,z*) => println("found tuple of unknown length")
| case _ => println("found unknown")
| }
matcher: (x: AnyRef)Unit
scala> matcher(res1)
found 3
Wed, 2010-03-31, 15:07
#2
Re: matching on tuples
You could check for tuples up to a certain size and then turn the rest into a list and match on that.
in your example case rest => rest.productIterator.toList match ...
-Stefan
2010/3/31 Michael Fortin <michael [at] m410 [dot] us>
in your example case rest => rest.productIterator.toList match ...
-Stefan
2010/3/31 Michael Fortin <michael [at] m410 [dot] us>
Is there a way to match on a tuple of unknown length? I'd like to do something like this, but of course, this doesn't compile. I'd like the third case to match if the tuple has more than 3 elements. Is that possible?
scala> def matcher(x:AnyRef) = x match {
| case (x,y) => println("found 2")
| case (x,y,z) => println("found 3")
| case (x,y,z*) => println("found tuple of unknown length")
| case _ => println("found unknown")
| }
matcher: (x: AnyRef)Unit
scala> matcher(res1)
found 3
Wed, 2010-03-31, 15:17
#3
Re: matching on tuples
On Wednesday March 31 2010, Sciss wrote:
> ... but then hundreds of other things derive from Product, ...
Including all case classes.
Randall Schulz
Wed, 2010-03-31, 17:27
#4
Re: matching on tuples
I've also found that not having a common superclass in tuples other than Product to be annoying.
On Wed, Mar 31, 2010 at 10:57 AM, Randall R Schulz <rschulz [at] sonic [dot] net> wrote:
On Wed, Mar 31, 2010 at 10:57 AM, Randall R Schulz <rschulz [at] sonic [dot] net> wrote:
On Wednesday March 31 2010, Sciss wrote:
> ... but then hundreds of other things derive from Product, ...
Including all case classes.
Randall Schulz
Wed, 2010-03-31, 21:57
#5
Re: matching on tuples
You can't do this out of the box, but you can do this with a little extra work (this is for 2.8; for 2.7, you need to drop the = 1 in n: Int = 1, and write your own thing to convert the remaining elements of the p into a list):
object Tuple2Plus {
def isTuple(ar: AnyRef,n: Int = 1) = {
val name = """scala\.Tuple(\d\d?)""".r.unapplySeq(ar.getClass.getName)
name.map(_.map(_.toInt).exists(i => i>=n && i<23)).getOrElse(false)
}
def unapply(a: Any): Option[(Any,Any,List[Any])] = {
if (isTuple(a.asInstanceOf[AnyRef],2)) {
val p = a.asInstanceOf[Product]
Some((
p.productElement(0),
p.productElement(1),
p.productIterator.toList.drop(2)
))
}
else None
}
}
Here's an example:
scala> ((1,2,"buckle","my","shoe"):Any) match {
| case (x,y) => println("A Tuple2")
| case (x,y,z) => println("A Tuple3")
| case Tuple2Plus(x: Int, y: Int, sl: List[_]) =>
| println("A Tuple2Plus with " + sl.length + " unbound elements")
| println(x + " plus " + y + " equals " + (x+y))
| case _ =>
| println("Not a tuple.")
| }
A Tuple2Plus with 3 unbound elements
1 plus 2 equals 3
Unfortunately, you'd need a Tuple2Plus, Tuple3Plus, etc., to cover all cases. (If you have more than one, you probably want to move isTuple to some common code. If you don't need to extract the initial N parts of the tuple, you could just use isTuple to know you've got one.)
--Rex
On Wed, Mar 31, 2010 at 9:43 AM, Michael Fortin <michael [at] m410 [dot] us> wrote:
object Tuple2Plus {
def isTuple(ar: AnyRef,n: Int = 1) = {
val name = """scala\.Tuple(\d\d?)""".r.unapplySeq(ar.getClass.getName)
name.map(_.map(_.toInt).exists(i => i>=n && i<23)).getOrElse(false)
}
def unapply(a: Any): Option[(Any,Any,List[Any])] = {
if (isTuple(a.asInstanceOf[AnyRef],2)) {
val p = a.asInstanceOf[Product]
Some((
p.productElement(0),
p.productElement(1),
p.productIterator.toList.drop(2)
))
}
else None
}
}
Here's an example:
scala> ((1,2,"buckle","my","shoe"):Any) match {
| case (x,y) => println("A Tuple2")
| case (x,y,z) => println("A Tuple3")
| case Tuple2Plus(x: Int, y: Int, sl: List[_]) =>
| println("A Tuple2Plus with " + sl.length + " unbound elements")
| println(x + " plus " + y + " equals " + (x+y))
| case _ =>
| println("Not a tuple.")
| }
A Tuple2Plus with 3 unbound elements
1 plus 2 equals 3
Unfortunately, you'd need a Tuple2Plus, Tuple3Plus, etc., to cover all cases. (If you have more than one, you probably want to move isTuple to some common code. If you don't need to extract the initial N parts of the tuple, you could just use isTuple to know you've got one.)
--Rex
On Wed, Mar 31, 2010 at 9:43 AM, Michael Fortin <michael [at] m410 [dot] us> wrote:
Is there a way to match on a tuple of unknown length? I'd like to do something like this, but of course, this doesn't compile. I'd like the third case to match if the tuple has more than 3 elements. Is that possible?
scala> def matcher(x:AnyRef) = x match {
| case (x,y) => println("found 2")
| case (x,y,z) => println("found 3")
| case (x,y,z*) => println("found tuple of unknown length")
| case _ => println("found unknown")
| }
matcher: (x: AnyRef)Unit
scala> matcher(res1)
found 3
the problem is that there is no common trait for tuples, e.g. Tuple1 extends Product1 with Product, and not Tuple1 extends Product1 with Product with TupleLike, so you could only test for the common superclass which is Product - but then hundreds of other things derive from Product, so that is probably useless....
Am 31.03.2010 um 14:43 schrieb Michael Fortin:
> Is there a way to match on a tuple of unknown length? I'd like to do something like this, but of course, this doesn't compile. I'd like the third case to match if the tuple has more than 3 elements. Is that possible?
>
> scala> def matcher(x:AnyRef) = x match {
> | case (x,y) => println("found 2")
> | case (x,y,z) => println("found 3")
> | case (x,y,z*) => println("found tuple of unknown length")
> | case _ => println("found unknown")
> | }
> matcher: (x: AnyRef)Unit
>
> scala> matcher(res1)
> found 3
>
>
>