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.
Related
Here's some code which looks reasonable enough to me:
val myMap: Map[Int, Int] = ((x: Int) => Map[Int, Int](1 -> x + 1, 2 -> x + 2))(4)
When I try to compile it, I get two errors like this:
Error:(20, 68) type mismatch;
found : Int(1)
required: String
val myMap: Map[Int, Int] = ((x: Int) => Map[Int, Int](1 -> x + 1, 2 -> x + 2))(4)
^
I understand that my compiler is trying to use the string addition implementation of +. But why is it doing that? How do I ask the compiler to use integer addition here?
(Changing Int to Integer doesn't help.)
Here is a simpler example that reproduces the error:
scala> def fail(x: Int) = 1 -> x + 1
<console>:10: error: type mismatch;
found : Int(1)
required: String
def fail(x: Int) = 1 -> x + 1
^
All of the operators in play are left-associative, so everything reads left-to-right. That is, the call chain looks like this:
1.->(x).+(1)
or using infix notation:
(1 -> x) + 1
1 -> x returns a tuple (1, x) and a tuple does not have a + method, so the compiler attempts to implicitly convert it to a String via toString, because String does have a + method, which eventually fails because Int is required.
Just use parentheses to group things appropriately:
scala> def foo(x: Int) = 1 -> (x + 1)
foo: (x: Int)(Int, Int)
'->' has the same precedence as '+', so 1 -> x + 1 parses as (1 -> x) + 1. Adding parentheses as 1 -> (x + 1) fixes this error.
The problem is that according to Scala's precedence rules, -> binds as tightly as +, because it starts with -.
It's fixed by adding parentheses:
val myMap: Map[Int, Int] = ((x: Int) => Map[Int, Int](1 -> (x + 1), 2 -> (x + 2)))(4)
When trying to remove all Unit - () from a list, I tried to call toMap.
scala> List((), ()).filter(_ != ()).toMap
<console>:8: error: Cannot prove that Unit <:< (T, U).
List((), ()).filter(_ != ()).toMap
^
What does this error mean?
For a List, I'd like to create a map of all tuples (String, String) for non-Unit elements, but some of the values can be null.
scala> val x = List((), (), (3,4)).filter(_ != ()).toMap
<console>:7: error: Cannot prove that Any <:< (T, U).
val x = List((), (), (3,4)).filter(_ != ()).toMap
^
scala> val x = List((), (), (3,4)).filter(_ != ())
x: List[Any] = List((3,4))
scala> x.toMap
<console>:9: error: Cannot prove that Any <:< (T, U).
x.toMap
^
Ah! Now your other question makes a little more sense. Still not sure what you're doing to produce this mixed Unit/Tuple2 list though.
This should work:
List((), (), (3,4)).collect { case t#(_: Int, _: Int) => t }.toMap
Note that I'm using variable binding here (binding the match to t) to return the same Tuple2 instance we matched rather than creating a new one.
By using collect you convert the type of your list from List[Any] to List[(Int, Int)], which is what toMap wants since it's expecting some List[(A,B)].
Note: Although this answer should work for you, I still think your design is flawed. You'd be better off fixing the underlying design flaw rather than treating the symptoms like this.
It looks like this would be a good fit for using Scala's Option type. In this case, your sample list would become List(None, None, Some((3,4))), or you could write it as List(None, None, Some(3->4)) for readability (nested parenthesis like that can get confusing).
If you use Option then the type of your list becomes List[Option[(Int, Int)]], which should be much nicer to deal with than a List[Any]. To get rid of the None entries and get the desired List[(Int,Int)] you can just call flatten:
List(None, None, Some(3->4)).flatten
// res0: List[(Int, Int)] = List((3,4))
List(None, None, Some(3->4)).flatten.toMap
// res1: scala.collection.immutable.Map[Int,Int] = Map(3 -> 4)
However, it would be even better if you can avoid putting the None entries in your list in the first place. If you're producing this list using a Scala for comprehension, you could use a guard in your for expression to remove the invalid elements from the output.
It means that the type of an element in the list can't be viewed as a tuple which is required to build a Map. A Map in a sense is a collection of tuples (and more).
Illustration:
scala> List(1).toMap
<console>:8: error: Cannot prove that Int <:< (T, U).
List(1).toMap
^
scala> List(1 -> 2).toMap
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2)
I can build a map from a list of tuples, but not from a list of single cardinality elements.
Maybe you mean to say .map instead of .toMap? ;)
All in one go:
scala> val l2 = List(1 -> 3, (), 4 -> 4, (), 9 -> 4, (), 16 -> 7)
l2: List[Any] = List((1,3), (), (4,4), (), (9,4), (), (16,7))
scala> (l2 collect { case (a, b) => (a, b) }).toMap
res4: scala.collection.immutable.Map[Any,Any] = Map(1 -> 3, 4 -> 4, 9 -> 4, 16 -> 7)
Better typed:
scala> (l2 collect { case (i: Int, j: Int) => (i, j) }).toMap
res5: scala.collection.immutable.Map[Int,Int] = Map(1 -> 3, 4 -> 4, 9 -> 4, 16 -> 7)
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))
I have a List of Map[Int, Int], that all have the same keys (from 1 to 20) and I'd like to merge their contents into a single Map[Int, Int].
I've read another post on stack overflow about merging maps that uses |+| from the scalaz library.
I've come up with the following solution, but it seems clunky to me.
val defaultMap = (2 to ceiling).map((_,0)).toMap
val factors: Map[Int, Int] = (2 to ceiling). map(primeFactors(_)).
foldRight(defaultMap)(mergeMaps(_, _))
def mergeMaps(xm: Map[Int, Int], ym: Map[Int, Int]): Map[Int,Int] = {
def iter(acc: Map[Int,Int], other: Map[Int,Int], i: Int): Map[Int,Int] = {
if (other.isEmpty) acc
else iter(acc - i + (i -> math.max(acc(i), other(i))), other - i, i + 1)
}
iter(xm, ym, 2)
}
def primeFactors(number: Int): Map[Int, Int] = {
def iter(factors: Map[Int,Int], rem: Int, i: Int): Map[Int,Int] = {
if (i > number) factors
else if (rem % i == 0) iter(factors - i + (i -> (factors(i)+1)), rem / i, i)
else iter(factors, rem, i + 1)
}
iter((2 to ceiling).map((_,0)).toMap, number, 2)
}
Explanation: val factors creates a list of maps that each represent the prime factors for the numbers from 2-20; then these 18 maps are folded into a single map containing the greatest value for each key.
UPDATE
Using the suggestion of #folone, I end up with the following code (a definite improvement over my original version, and I don't have to change the Maps to HashMaps):
import scalaz._
import Scalaz._
import Tags._
/**
* Smallest Multiple
*
* 2520 is the smallest number that can be divided by each of the numbers
* from 1 to 10 without any remainder. What is the smallest positive number
* that is evenly divisible by all of the numbers from 1 to 20?
*
* User: Alexandros Bantis
* Date: 1/29/13
* Time: 8:07 PM
*/
object Problem005 {
def findSmallestMultiple(ceiling: Int): Int = {
val factors = (2 to ceiling).map(primeFactors(_).mapValues(MaxVal)).reduce(_ |+| _)
(1 /: factors.map(m => intPow(m._1, m._2)))(_ * _)
}
private def primeFactors(number: Int): Map[Int, Int] = {
def iter(factors: Map[Int,Int], rem: Int, i: Int): Map[Int,Int] = {
if (i > number) factors.filter(_._2 > 0).mapValues(MaxVal)
else if (rem % i == 0) iter(factors - i + (i -> (factors(i)+1)), rem / i, i)
else iter(factors, rem, i + 1)
}
iter((2 to number).map((_,0)).toMap, number, 2)
}
private def intPow(x: Int, y: Int): Int = {
def iter(acc: Int, rem: Int): Int = {
if (rem == 0) acc
else iter(acc * x, rem -1)
}
if (y == 0) 1 else iter(1, y)
}
}
This solution does not work for general Maps, but if you are using immutable.HashMaps you may consider the merged method:
def merged[B1 >: B](that: HashMap[A, B1])(mergef: ((A, B1), (A, B1)) ⇒ (A, B1)): HashMap[A, B1]
Creates a new map which is the merge of this and the argument hash
map.
Uses the specified collision resolution function if two keys are the
same. The collision resolution function will always take the first
argument from this hash map and the second from that.
The merged method is on average more performant than doing a traversal
and reconstructing a new immutable hash map from scratch, or ++.
Use case:
val m1 = immutable.HashMap[Int, Int](1 -> 2, 2 -> 3)
val m2 = immutable.HashMap[Int, Int](1 -> 3, 4 -> 5)
m1.merged(m2) {
case ((k1, v1), (k2, v2)) => ((k1, math.max(v1, v2)))
}
As your tags suggest, you might be interested in a scalaz solution. Here goes:
> console
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.10.0 (OpenJDK 64-Bit Server VM, Java 1.7.0_15).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scalaz._, Scalaz._, Tags._
import scalaz._
import Scalaz._
import Tags._
There exists a Semigroup instance for Ints under a maximum operation:
scala> Semigroup[Int ## MaxVal]
res0: scalaz.Semigroup[scalaz.##[Int,scalaz.Tags.MaxVal]] = scalaz.Semigroup$$anon$9#15a9a9c6
Let's just use it:
scala> val m1 = Map(1 -> 2, 2 -> 3) mapValues MaxVal
m1: scala.collection.immutable.Map[Int,scalaz.##[Int,scalaz.Tags.MaxVal]] = Map(1 -> 2, 2 -> 3)
scala> val m2 = Map(1 -> 3, 4 -> 5) mapValues MaxVal
m2: scala.collection.immutable.Map[Int,scalaz.##[Int,scalaz.Tags.MaxVal]] = Map(1 -> 3, 4 -> 5)
scala> m1 |+| m2
res1: scala.collection.immutable.Map[Int,scalaz.##[Int,scalaz.Tags.MaxVal]] = Map(1 -> 3, 4 -> 5, 2 -> 3)
If you're interested in how this "tagging" (the ## thing) works, here's a good explanation: http://etorreborre.blogspot.de/2011/11/practical-uses-for-unboxed-tagged-types.html
Starting Scala 2.13, another solution only based on the standard library consists in merging the Maps as sequences before applying a groupMapReduce which (as its name suggests) is an equivalent of a groupBy followed by a mapping and a reduce step on values:
// val map1 = Map(1 -> 2, 2 -> 3)
// val map2 = Map(1 -> 3, 4 -> 5)
(map1.toSeq ++ map2).groupMapReduce(_._1)(_._2)(_ max _)
// Map[Int,Int] = Map(2 -> 3, 4 -> 5, 1 -> 3)
This:
concatenates the two maps as a sequence of tuples (List((1,2), (2,3), (1,3), (4,5))). For conciseness, map2 is implicitly converted to Seq to adopt the type of map1.toSeq - but you could choose to make it explicit by using map2.toSeq.
groups elements based on their first tuple part (group part of groupMapReduce)
maps grouped values to their second tuple part (map part of groupMapReduce)
reduces mapped values (_ max _) by taking their max (reduce part of groupMapReduce)
Inside a function of mine I construct a result set by filling a new mutable HashMap with data (if there is a better way - I'd appreciate comments). Then I'd like to return the result set as an immutable HashMap. How to derive an immutable from a mutable?
Discussion about returning immutable.Map vs. immutable.HashMap notwithstanding, what about simply using the toMap method:
scala> val m = collection.mutable.HashMap(1 -> 2, 3 -> 4)
m: scala.collection.mutable.HashMap[Int,Int] = Map(3 -> 4, 1 -> 2)
scala> m.toMap
res22: scala.collection.immutable.Map[Int,Int] = Map(3 -> 4, 1 -> 2)
As of 2.9, this uses the method toMap in TraversableOnce, which is implemented as follows:
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = {
val b = immutable.Map.newBuilder[T, U]
for (x <- self)
b += x
b.result
}
scala> val m = collection.mutable.HashMap(1->2,3->4)
m: scala.collection.mutable.HashMap[Int,Int] = Map(3 -> 4, 1 -> 2)
scala> collection.immutable.HashMap() ++ m
res1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 3 -> 4)
or
scala> collection.immutable.HashMap(m.toSeq:_*)
res2: scala.collection.immutable.HashMap[Int,Int] = Map(1 -> 2, 3 -> 4)
If you have a map : logMap: Map[String, String]
just need to do : logMap.toMap()