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?
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
#Eastsun's answer nicely explains the first part of your question "how does Scala sort tuples".
Regarding the second part "why doesn't tuple have a < method": In Scala, comparators like < either translate to native JVM comparisons for basic types (when comparing Int or Double etc) or to member functions of someClass with the type <(that: someClass): Boolean. This case is actually just syntactic sugar: someObject < otherObject translates to someObject.<(otherObject). If you want to have this functionality for tuples, you can bring an implicit class into scope, and map the comparison member function to the comparators provided by the Ordering:
implicit class ProvideComparator[T](t1: T)(implicit ord: Ordering[T]) {
def <(t2: T) = ord.lt(t1, t2)
def >(t2: T) = ord.gt(t1, t2) // and so on
}
Now you can just write:
scala> (1,2) < (2,2)
res2: Boolean = true
Related
On the Scala command line, there is no problem writing:
List((1,2),(1,'a'))
But I can't seem to write a function that would convert a Tuple2 to a List, because Tuple2 takes 2 type parameters, but List only one. Any attempt such as:
def tuple2ToList[T1, T2](pair: (T1, T2)): List[Any] = List(pair._1, pair._2)
seems to be bound to lose type information. Is there anything we can do to preserve some type information in the process?
This is a solution proposed by #BenReich in a comment
Simply use one type parameter instead of two, the compiler will
automatically select the least upper bound of the types of the two
tuple elements:
def tupleToList[T](p: (T, T)): List[T] = List(p._1, p._2)
Examples:
scala> tupleToList((Some(42), None))
res4: List[Option[Int]] = List(Some(42), None)
scala> tupleToList((0.9999, 1))
res5: List[AnyVal] = List(0.9999, 1)
scala> tupleToList((Set(1), List(1)))
res6: List[scala.collection.immutable.Iterable[Int] with Int => AnyVal] = List(Set(1), List(1))
This is the old suboptimal solution, I'll leave it here as context for #BenReich's comment.
Define the return type as the least upper bound of T1 and T2:
def tupleToListRescueTypeInfo[R, T1 <: R, T2 <: R](p: (T1, T2)): List[R] =
List(p._1, p._2)
Little test:
scala> tupleToListRescueTypeInfo((2, 3))
res0: List[Int] = List(2, 3)
scala> tupleToListRescueTypeInfo((Some[Int](3), None))
res1: List[Option[Int]] = List(Some(3), None)
scala> tupleToListRescueTypeInfo((List(1,2), Set(1,2)))
res2: List[scala.collection.immutable.Iterable[Int] with Int => AnyVal] =
List(List(1, 2), Set(1, 2))
It obviously cannot preserve all type information, but it at least attempts to rescue as much as possible.
I have several methods that operate on Vector sequences and the following idiom is common when combining data from multiple vectors into a single one with the use of a for comprehension / yield:
(for (i <- 0 until y.length) yield y(i) + 0.5*dy1(i)) toVector
Notice the closing toVector and the enclosing parentheses around the for comprehension. I want to get rid of it because it's ugly, but removing it produces the following error:
type mismatch;
found : scala.collection.immutable.IndexedSeq[Double]
required: Vector[Double]
Is there a better way of achieving what I want that avoids explicitly calling toVector many times to essentially achieve a non-operation (converting and indexed sequence...to an indexed sequence)?
One way to avoid collection casting, e.g. toVector, is to invoke, if possible, only those methods that return the same collection type.
y.zipWithIndex.map{case (yv,idx) => yv + 0.5*dy1(idx)}
for yield on Range which you are using in your example yields a Vector[T] by default.
example,
scala> val squares= for (x <- Range(1, 3)) yield x * x
squares: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 4)
check the type,
scala> squares.isInstanceOf[Vector[Int]]
res14: Boolean = true
Note that Vector[T] also extends IndexedSeq[T].
#SerialVersionUID(-1334388273712300479L)
final class Vector[+A] private[immutable] (private[collection] val startIndex: Int, private[collection] val endIndex: Int, focus: Int)
extends AbstractSeq[A]
with IndexedSeq[A]
with GenericTraversableTemplate[A, Vector]
with IndexedSeqLike[A, Vector[A]]
with VectorPointer[A #uncheckedVariance]
with Serializable
with CustomParallelizable[A, ParVector[A]]
That's why above result is also an instance of IndexedSeq[T],
scala> squares.isInstanceOf[IndexedSeq[Int]]
res15: Boolean = true
You can define the type of your result as IndexedSeq[T] and still achieve what you want with Vector without explicitly calling .toVector
scala> val squares: IndexedSeq[Int] = for (x <- Range(1, 3)) yield x * x
squares: IndexedSeq[Int] = Vector(1, 4)
scala> squares == Vector(1, 4)
res16: Boolean = true
But for yield on Seq[T] gives List[T] by default.
scala> val squares = for (x <- Seq(1, 3)) yield x * x
squares: Seq[Int] = List(1, 9)
Only in that case if you want vector you must .toVector the result.
scala> squares.isInstanceOf[Vector[Int]]
res21: Boolean = false
scala> val squares = (for (x <- Seq(1, 3)) yield x * x).toVector
squares: Vector[Int] = Vector(1, 9)
I want to find the Tuple with the largest second element:
mylist.reduce { (x, y) => {
if (y._1 > x._1) y
else x
}}
Where x and y are of type Tuple3[DenseVector[Int], Double, PipelineModel].
I get the error that > cannot be resolved. What's up with that? Using foldLeft and providing a zero element did not help either.
Can I write the code nicer? (It doesn't look so nice, I think.)
In a triplet (a, b, c) triplet._2 gives you the second element.
_1 gives first element
_2 gives second element
_3 gives third element
Tuples are not zero based.
scala> val triplet = (1, 2, 3)
triplet: (Int, Int, Int) = (1,2,3)
scala> triplet._1
res0: Int = 1
scala> triplet._2
res1: Int = 2
scala> triplet._3
res2: Int = 3
Answer 1:
In your case triplet._1 gives the first element of the triplet (tuple3) which is DenseVector[Int] element on which you cannot use >. Thats why > is not resolved.
Answer 2:
maxBy
l.maxBy(_._2)
Scala REPL
scala> val l = List((1, 2, 3), (0, 0, 1))
l: List[(Int, Int, Int)] = List((1,2,3), (0,0,1))
scala> l.maxBy(_._2)
res1: (Int, Int, Int) = (1,2,3)
Reduce
l.reduce { (x, y) => if (x._2 > y._2) x else y }
Scala REPL
scala> val l = List((1, 2, 3), (0, 0, 1))
l: List[(Int, Int, Int)] = List((1,2,3), (0,0,1))
scala> l.reduce { (x, y) => if (x._2 > y._2) x else y }
res3: (Int, Int, Int) = (1,2,3)
Is there a easy way to add tuples which contain addable elements like Int, Doubles etc?
For examples,
(1,2) + (1,3) = (2,5)
Scalaz
import scalaz._, Scalaz._
scala> (1, 2.5) |+| (3, 4.4)
res0: (Int, Double) = (4,6.9)
There is an operator |+| for any class A with implicit Semigroup[A] in scope. For Int |+| is + by default (you could redefine it in your code).
There is an implicit Semigroup[(A, B)] for all tuples if there is implicit Semigroup for A and B.
See Scalaz cheat sheet.
+1 for the the Scalaz answer :-)
If you want a very simple version of it you could define an implicit class like:
implicit class TuppleAdd(t: (Int, Int)) {
def +(p: (Int, Int)) = (p._1 + t._1, p._2 + t._2)
}
(1, 1) + (2, 2) == (3, 3)
// update1, more generic version for numbers:
So this is the simplest version, defined only for Ints, we could generify it for all numeric values using Scala's Numeric:
implicit class Tupple2Add[A : Numeric, B : Numeric](t: (A, B)) {
import Numeric.Implicits._
def + (p: (A, B)) = (p._1 + t._1, p._2 + t._2)
}
(2.0, 1) + (1.0, 2) == (3.0, 3)
This works with Cats as well, and similar to the scalaz answer:
> (1,2) |+| (1,3)
res3: (Int, Int) = (2, 5)
And likewise, it relies on Semigroup.
I'm new to scala, and what I'm learning is tuple.
I can define a tuple as following, and get the items:
val tuple = ("Mike", 40, "New York")
println("Name: " + tuple._1)
println("Age: " + tuple._2)
println("City: " + tuple._3)
My question is:
How to get the length of a tuple?
Is tuple mutable? Can I modify its items?
Is there any other useful operation we can do on a tuple?
Thanks in advance!
1] tuple.productArity
2] No.
3] Some interesting operations you can perform on tuples: (a short REPL session)
scala> val x = (3, "hello")
x: (Int, java.lang.String) = (3,hello)
scala> x.swap
res0: (java.lang.String, Int) = (hello,3)
scala> x.toString
res1: java.lang.String = (3,hello)
scala> val y = (3, "hello")
y: (Int, java.lang.String) = (3,hello)
scala> x == y
res2: Boolean = true
scala> x.productPrefix
res3: java.lang.String = Tuple2
scala> val xi = x.productIterator
xi: Iterator[Any] = non-empty iterator
scala> while(xi.hasNext) println(xi.next)
3
hello
See scaladocs of Tuple2, Tuple3 etc for more.
One thing that you can also do with a tuple is to extract the content using the match expression:
def tupleview( tup: Any ){
tup match {
case (a: String, b: String) =>
println("A pair of strings: "+a + " "+ b)
case (a: Int, b: Int, c: Int) =>
println("A triplet of ints: "+a + " "+ b + " " +c)
case _ => println("Unknown")
}
}
tupleview( ("Hello", "Freewind"))
tupleview( (1,2,3))
Gives:
A pair of strings: Hello Freewind
A triplet of ints: 1 2 3
Tuples are immutable, but, like all cases classes, they have a copy method that can be used to create a new Tuple with a few changed elements:
scala> (1, false, "two")
res0: (Int, Boolean, java.lang.String) = (1,false,two)
scala> res0.copy(_2 = true)
res1: (Int, Boolean, java.lang.String) = (1,true,two)
scala> res1.copy(_1 = 1f)
res2: (Float, Boolean, java.lang.String) = (1.0,true,two)
Concerning question 3:
A useful thing you can do with Tuples is to store parameter lists for functions:
def f(i:Int, s:String, c:Char) = s * i + c
List((3, "cha", '!'), (2, "bora", '.')).foreach(t => println((f _).tupled(t)))
//--> chachacha!
//--> borabora.
[Edit] As Randall remarks, you'd better use something like this in "real life":
def f(i:Int, s:String, c:Char) = s * i + c
val g = (f _).tupled
List((3, "cha", '!'), (2, "bora", '.')).foreach(t => println(g(t)))
In order to extract the values from tuples in the middle of a "collection transformation chain" you can write:
val words = List((3, "cha"),(2, "bora")).map{ case(i,s) => s * i }
Note the curly braces around the case, parentheses won't work.
Another nice trick ad question 3) (as 1 and 2 are already answered by others)
val tuple = ("Mike", 40, "New York")
tuple match {
case (name, age, city) =>{
println("Name: " + name)
println("Age: " + age)
println("City: " + city)
}
}
Edit: in fact it's rather a feature of pattern matching and case classes, a tuple is just a simple example of a case class...
You know the size of a tuple, it's part of it's type. For example if you define a function def f(tup: (Int, Int)), you know the length of tup is 2 because values of type (Int, Int) (aka Tuple2[Int, Int]) always have a length of 2.
No.
Not really. Tuples are useful for storing a fixed amount of items of possibly different types and passing them around, putting them into data structures etc. There's really not much you can do with them, other than creating tuples, and getting stuff out of tuples.
1 and 2 have already been answered.
A very useful thing that you can use tuples for is to return more than one value from a method or function. Simple example:
// Get the min and max of two integers
def minmax(a: Int, b: Int): (Int, Int) = if (a < b) (a, b) else (b, a)
// Call it and assign the result to two variables like this:
val (x, y) = minmax(10, 3) // x = 3, y = 10
Using shapeless, you easily get a lot of useful methods, that are usually available only on collections:
import shapeless.syntax.std.tuple._
val t = ("a", 2, true, 0.0)
val first = t(0)
val second = t(1)
// etc
val head = t.head
val tail = t.tail
val init = t.init
val last = t.last
val v = (2.0, 3L)
val concat = t ++ v
val append = t :+ 2L
val prepend = 1.0 +: t
val take2 = t take 2
val drop3 = t drop 3
val reverse = t.reverse
val zip = t zip (2.0, 2, "a", false)
val (unzip, other) = zip.unzip
val list = t.toList
val array = t.toArray
val set = t.to[Set]
Everything is typed as one would expect (that is first has type String, concat has type (String, Int, Boolean, Double, Double, Long), etc.)
The last method above (.to[Collection]) should be available in the next release (as of 2014/07/19).
You can also "update" a tuple
val a = t.updatedAt(1, 3) // gives ("a", 3, true, 0.0)
but that will return a new tuple instead of mutating the original one.