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.
Related
I've notice this behavior in Scala
val list = List[(Int, Int)]()
val set = HashSet[(Int, Int)]()
scala> list :+ (1, 2)
res30: List[(Int, Int)] = List((1,2))
scala> list :+ (1 -> 2)
res31: List[(Int, Int)] = List((1,2))
scala> list :+ 1 -> 2
res32: List[(Int, Int)] = List((1,2))
//Work
// But the same for set not work
set += (1, 2)
<console>:14: error: type mismatch;
found : Int(2)
required: (Int, Int)
set += (1, 2)
//Ok may be += in set mean add all that mean this should work
set += ((1, 2))
set += ((1, 2), (3,4))
// Worked
// But why this one work
set += 1 -> 2
set += (1 -> 2)
set += ((1 -> 2))
Now I'm confuse, could you explain why tuple is not tuple?
scala> (4->5).getClass
res28: Class[_ <: (Int, Int)] = class scala.Tuple2
scala> (4,7).getClass
res29: Class[_ <: (Int, Int)] = class scala.Tuple2$mcII$sp
The parser stage -Xprint:parser gives
set.$plus$eq(1, 2)
which seems to resolve to
def += (elem1: A, elem2: A, elems: A*)
that is a method that accepts multiple arguments so compiler probably thinks elem1 = 1 or elem2 = 2 instead of considering (1,2) as a tuple.
missingfaktor points to SLS 6.12.3 Infix Operations as the explanation
The right-hand operand of a left-associative operator may consist of
several arguments enclosed in parentheses, e.g. π;op;(π1,β¦,ππ).
This expression is then interpreted as π.op(π1,β¦,ππ).
Now the operator += is left-associative because it does not end in :, and the right-hand operand of += consists of several arguments enclosed in parentheses (1,2). Therefore, by design, the compiler does not treat (1,2) as Tuple2.
I think the difference is that HashSet[T] defines two overloads for +=, one of which takes a single T, and the other takes multiple (as a T* params list). This is inherited from Growable[T], and shown here.
List[T].:+ can only take one T on the right hand side, which is why the compiler works out that it's looking at a tuple, not something that should be turned into a params list.
If you do set += ((1, 2)) then it compiles. Also, val tuple = (1,2); set += x works too.
See Mario Galicβs answer for why in the case of HashSet[T].+= the compiler chooses the overload that can't type over the one that can.
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'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
I am getting a taste of Scala through the artima "Programming in Scala" book.
While presenting the Map traits, the authors go to some lengths to describe the -> syntax as a method that can be applied to any type to get a tuple.
And indeed:
scala> (2->"two")
res1: (Int, String) = (2,two)
scala> (2,"two")
res2: (Int, String) = (2,two)
scala> (2->"two") == (2, "two")
res3: Boolean = true
But those are not equivalent:
scala> Map(1->"one") + (2->"two")
res4: scala.collection.immutable.Map[Int,String] = Map(1 -> one, 2 -> two)
scala> Map(1->"one") + (2, "two")
<console>:8: error: type mismatch;
found : Int(2)
required: (Int, ?)
Map(1->"one") + (2, "two")
Why is this so, since my first tests seem to show that both "pair" syntaxes build a tuple?
Regards.
They are exactly the same, thanks to this class in Predef (only partly reproduced here):
final class ArrowAssoc[A](val __leftOfArrow: A) extends AnyVal {
#inline def -> [B](y: B): Tuple2[A, B] = Tuple2(__leftOfArrow, y)
}
#inline implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)
So now the question is when will (a,b) syntax be ambiguous where (a -> b) is not? And the answer is in function calls, especially when they're overloaded:
def f[A](a: A) = a.toString
def f[A,B](a: A, b: B) = a.hashCode + b.hashCode
f(1,2) // Int = 3
f(1 -> 2) // String = (1,2)
f((1, 2)) // String = (1,2)
Map + in particular gets confused because it's overloaded with a multiple-argument version, so you could
Map(1 -> 2) + (3 -> 4, 4 -> 5, 5 -> 6)
and it thus interprets
Map(1 -> 2) + (3, 4)
as trying to add both 3 to the map, and then 4 to the map. Which of course makes no sense, but it doesn't try the other interpretation.
With -> there is no such ambiguity.
However, you can't
Map(1 -> 2) + 3 -> 4
because + and - have the same precedence. Thus it is interpreted as
(Map(1 -> 2) + 3) -> 4
which again fails because you're trying to add 3 in place of a key-value pair.
Scala REPL gives the same type for both expressions - (tuple? -- strange!). Yet ("a" ->1) which is a Map I can add to map and ("a", 1)can not. Why Scala REPL shows tuple type type for Map expression?
scala> :t ("a" -> 1)
(String, Int)
scala> :t ("a",1)
(String, Int)
scala> val m = Map.empty[String, Int]
m: scala.collection.immutable.Map[String,Int] = Map()
scala> m + ("a",1)
<console>:9: error: type mismatch;
found : String("a")
required: (String, ?)
m + ("a",1)
^
scala> m + ("a" ->1)
res19: scala.collection.immutable.Map[String,Int] = Map(a -> 1)
Scala thinks a + (b,c) means you are trying to call the + method with two arguments, which is a real possibility since maps do have a multi-argument addition method so you can do things like
m + (("a" -> 1), ("b" -> 2))
the solution is simple: just add an extra set of parentheses so it's clear that (b,c) is in fact a tuple being passed as a single argument.
m + (("a", 1))
Actually, the reason for this is that Predef: http://www.scala-lang.org/api/current/index.html#scala.Predef$ (which is always in scope in Scala) contains an implicit conversion from Any to ArrowAssoc (the method implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A])
ArrowAssoc contains the method -> which converts it to a tuple.
So basically you are doing any2ArrowAssoc("a").->(1) which returns ("a",1).
From repl:
any2ArrowAssoc("a").->(1)
res1: (java.lang.String, Int) = (a,1)
Furthermore, you can work on immutable hashmaps like this:
val x = HashMap[Int,String](1 -> "One")
x: scala.collection.immutable.HashMap[Int,String] = Map((1,One))
val y = x ++ HashMap[Int,String](2 -> "Two")
y: scala.collection.immutable.Map[Int,String] = Map((1,One), (2,Two))
val z = x + (3 -> "Three")
z: scala.collection.immutable.HashMap[Int,String] = Map((1,One), (3,Three))