This page is no longer maintained — Please continue to the home page at www.scala-lang.org

matching on tuples

5 replies
Michael Fortin
Joined: 2010-02-16,
User offline. Last seen 42 years 45 weeks ago.

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

Sciss
Joined: 2008-12-17,
User offline. Last seen 28 weeks 5 days ago.
Re: matching on tuples

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
>
>
>

Stefan Langer
Joined: 2009-10-23,
User offline. Last seen 42 years 45 weeks ago.
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>
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



Randall R Schulz
Joined: 2008-12-16,
User offline. Last seen 1 year 29 weeks ago.
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

Rodrigo Cano
Joined: 2009-03-22,
User offline. Last seen 42 years 45 weeks ago.
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 Wednesday March 31 2010, Sciss wrote:
> ... but then hundreds of other things derive from Product, ...

Including all case classes.


Randall Schulz

ichoran
Joined: 2009-08-14,
User offline. Last seen 2 years 3 weeks ago.
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:
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



Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland