I'm trying to understand how scala handles ordering and sorting of tuples
For example, if I got the list
val l = for {i <- 1 to 5} yield (-i,i*2)
Vector((-1,2), (-2,4), (-3,6), (-4,8), (-5,10))
scala knows how to sort it:
l.sorted
Vector((-5,10), (-4,8), (-3,6), (-2,4), (-1,2))
But tuple don't have a '<' method:
l.sortWith(_ < _)
error: value < is not a member of (Int, Int)
l.sortWith(_ < _)
How does scala know how to sort those tuples?
In Scala, a tuple is a value that contains a fixed number of elements, each with its own type. Tuples are immutable.
Tuple is a collection of elements. Tuples are heterogeneous data structures, i.e., is they can store elements of different data types. A tuple is immutable, unlike an array in scala which is mutable.
The word "order", when it comes to tuples and sequences, does not refer to any pattern found in the numbers contained in the tuple. The elements of a tuple do not necessarily have to be increasing or decreasing.
In Scala we do not sort Lists in-place. They are immutable. But we use lambda expressions, and the Ordering type, to create sorted copies of these lists.
Because sorted
have an implicit parameter ord
:
def sorted[B >: A](implicit ord: math.Ordering[B]): List[A] Sorts this sequence according to an Ordering.
The sort is stable. That is, elements that are equal (as determined by lt) appear in the same order in the sorted sequence as in the original.
ord the ordering to be used to compare elements.
and there is an implicit conversion defined in scala.math.Ordering
:
implicit def Tuple2[T1, T2](implicit ord1: Ordering[T1],
ord2: Ordering[T2]): Ordering[(T1, T2)]
So l.sorted
will be transformed to l.sorted(scala.math.Ordering.Tuple2[Int, Int]())
.
Test it:
scala> def catchOrd[A](xs: A)(implicit ord: math.Ordering[A]) = ord
catchOrd: [A](xs: A)(implicit ord: scala.math.Ordering[A])scala.math.Ordering[A]
scala> catchOrd((1,2))
res1: scala.math.Ordering[(Int, Int)] = scala.math.Ordering$$anon$11@11bbdc80
And of course, you can defined your own Ordering
:
scala> implicit object IntTupleOrd extends math.Ordering[(Int, Int)] {
| def compare(x: (Int, Int), y: (Int, Int)): Int = {
| println(s"Hi, I am here with x: $x, y: $y")
| val a = x._1*x._2
| val b = y._1*y._2
| if(a > b) 1 else if(a < b) -1 else 0
| }
| }
defined object IntTupleOrd
scala> Seq((1, 10), (3, 4), (2, 3)).sorted
Hi, I am here with x: (1,10), y: (3,4)
Hi, I am here with x: (3,4), y: (2,3)
Hi, I am here with x: (1,10), y: (2,3)
res2: Seq[(Int, Int)] = List((2,3), (1,10), (3,4))
EDIT There is a short way to make Tuple[Int, Int]
support all the following methods: <
, <=
, >
, >=
.
scala> implicit def mkOps[A](x: A)(implicit ord: math.Ordering[A]): ord.Ops =
| ord.mkOrderingOps(x)
mkOps: [A](x: A)(implicit ord: scala.math.Ordering[A])ord.Ops
scala> (1, 2) < (3, 4)
res0: Boolean = true
scala> (1, 2) <= (3, 4)
res1: Boolean = true
scala> (1, 2, 3) <= (1, 2, 4)
res2: Boolean = true
scala> (3, 3, 3, 3) >= (3, 3, 3, 4)
res3: Boolean = false
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With