Scala: flatMap with tuples - scala

Why is the below statement valid for .map() but not for .flatMap()?
val tupled = input.map(x => (x*2, x*3))
//Compilation error: cannot resolve reference flatMap with such signature
val tupled = input.flatMap(x => (x*2, x*3))
This statement has no problem, though:
val tupled = input.flatMap(x => List(x*2, x*3))

Assuming input if of type List[Int], map takes a function from Int to A, whereas flatMap takes a function from Int to List[A].
Depending on your use case you can choose either one or the other, but they're definitely not interchangeable.
For instance, if you are merely transforming the elements of a List you typically want to use map:
List(1, 2, 3).map(x => x * 2) // List(2, 4, 6)
but you want to change the structure of the List and - for example - "explode" each element into another list then flattening them, flatMap is your friend:
List(1, 2, 3).flatMap(x => List.fill(x)(x)) // List(1, 2, 2, 3, 3, 3)
Using map you would have had List(List(1), List(2, 2), List(3, 3, 3)) instead.

To understand how this is working, it can be useful to explicitly unpack the functions you're sending to map and flatMap and examine their signatures. I've rewritten them here, so you can see that f is a function mapping from Int to an (Int, Int) tuple, and g is a function that maps from Int to a List[Int].
val f: (Int) => (Int, Int) = x => (x*2, x*3)
val g: (Int) => List[Int] = x => List(x*2, x*3)
List(1,2,3).map(f)
//res0: List[(Int, Int)] = List((2,3), (4,6), (6,9))
List(1,2,3).map(g)
//res1: List[List[Int]] = List(List(2, 3), List(4, 6), List(6, 9))
//List(1,2,3).flatMap(f) // This won't compile
List(1,2,3).flatMap(g)
//res2: List[Int] = List(2, 3, 4, 6, 6, 9)
So why won't flatMap(f) compile? Let's look at the signature for flatMap, in this case pulled from the List implementation:
final override def flatMap[B, That](f : scala.Function1[A, scala.collection.GenTraversableOnce[B]])(...)
This is a little difficult to unpack, and I've elided some of it, but the key is the GenTraversableOnce type. List, if you follow it's chain of inheritance up, has this as a trait it is built with, and thus a function that maps from some type A to a List (or any object with the GenTraversableOnce trait) will be a valid function. Notably, tuples do not have this trait.
That is the in-the-weeds explanation why the typing is wrong, and is worth explaining because any error that says 'cannot resolve reference with such a signature' means that it can't find a function that takes the explicit type you're offering. Types are very often inferred in Scala, and so you're well-served to make sure that the type you're giving is the type expected by the method you're calling.
Note that flatMap has a standard meaning in functional programming, which is, roughly speaking, any mapping function that consumes a single element and produces n elements, but your final result is the concatenation of all of those lists. Therefore, the function you pass to flatMap will always expect to produce a list, and no flatMap function would be expected to know how to act on single elements.

Related

Add an element to each component of a sequence

I was wondering if a function exists in Scala that does this:
Seq[T] x A => Seq[T x A]
Or an example:
(1,2,3) 4 -> ((1,4), (2,4), (3,4))
I realized I could do this by simply looking up Scala standard collection reference, yet there does not seem to be an easy way of looking by type (as opposed to hoogle). Of course I could also write my own combinator.
If you want to do it lazily you can do:
def zipWith[T, A](s: Seq[T], a: A): LazyZip2[T, A, s.type] = {
s.lazyZip(LazyList.continually(a))
}
Please note that this results in an unmaterialized value. In order to get the results you need to materialize them. For example, the following:
println(zipWith(List(1, 2, 3), 4).toSeq)
will output:
List((1,4), (2,4), (3,4))
while
println(zipWith(List(1, 2, 3), 4))
outputs:
List(1, 2, 3).lazyZip(LazyList(<not computed>))

How to pass ordering to scala.util.Sorting.quickSort

I'm trying to pass reverse ordering to the scala.util.Sorting.quickSort with this code:
val a = Array(3, 5, 1, 2)
scala.util.Sorting.quickSort(a)(Ordering.Int.reverse)
It doesn't work saying that:
Error: Unit does not take parameters
However, the code like that works:
val a = Array(3, 5, 1, 2)
a.sorted(Ordering.Int.reverse)
I don't understand why the quickSort example doesn't work? The Ordering.Int.reverse produce Ordering[Int] and according to documentation, the quickSort accepts it implicitly.
I'm running with Scala 2.12.7.
Expanding on Yuval Itzchakov's comment, here is the source code for scala.util.Sorting.quickSort:
def quickSort(a: Array[Int]): Unit = java.util.Arrays.sort(a)
A few lines down, another overloaded quicksort is defined as:
def quickSort[K: Ordering](a: Array[K]): Unit = {...
You mean to use this latter one. But when you call quickSort, the compiler is picking the first one which does not accept an Ordering in its parameter list so it complains about extra parameters. You will need to specify type parameter if you are using Int, Double, or Float as explained here. To summarize:
val a : Array[Int] = Array(3, 5, 1, 2)
val b = Array(3L, 5L, 1L, 2L) //Longs do not have this problem!
scala.util.Sorting.quickSort[Int](a)(Ordering.Int.reverse)
scala.util.Sorting.quickSort(b)(Ordering.Long.reverse)
println(a.toList)
println(b.toList)
List(5, 3, 2, 1)
List(5, 3, 2, 1)
According to documentation the signature of method that uses Ordering is:
def quickSort[K](a: Array[K])(implicit arg0: math.Ordering[K]): Unit
Sort array a with quicksort, using the Ordering on its elements. This
algorithm sorts in place, so no additional memory is used aside from
what might be required to box individual elements during comparison.
try this:
scala> val a = Array(3, 5, 1, 2)
a: Array[Int] = Array(3, 5, 1, 2)
scala> scala.util.Sorting.quickSort(a)(Ordering.Int.reverse)
<console>:13: error: Unit does not take parameters
scala.util.Sorting.quickSort(a)(Ordering.Int.reverse)
^
scala> scala.util.Sorting.quickSort[Int](a)(Ordering[Int].reverse)
scala> a
res2: Array[Int] = Array(5, 3, 2, 1)

Using `Seq::fill` with an existing sequence

I would want to fill a Seq[Seq[Int]] instance with an existing Seq[Int] by using the method fill(n1, n2)(element : A) of the Object Seq, as detailed in the documentation : http://www.scala-lang.org/api/2.12.3/scala/collection/Seq$.html#fillA(elem:=>A):CC[CC[A]]
So I wrote this call :
Seq.fill(width, width)(_random_values.) , with _random_values the existing Seq[Int].
The problem is that the parameter of the second list of fill is an element, not a Seq. So, what could I type to iterate on each _random_values's integers AND to execute the Seq.fill for each current integer ?
Seq.fill is more appropriate to create a Seq base on a static value. For your use case Seq.grouped might be a better fit:
val values: Seq[Int] = List(1, 2, 3, 4)
val result: Seq[Seq[Int]] = values.grouped(2).toSeq
result.foreach(println)
/*
List(1, 2)
List(3, 4)
*/

flatten and flatMap in scala

I'll like to check if I have understood flatten and flatMap functions correctly.
1) Am I correct that flatten works only when a collection constitutes of other collections. Eg flatten would work on following lists
//list of lists
val l1 = List(List(1,1,2,-1,3,1,-4,5), List("a","b"))
//list of a set, list and map
val l2 = List(Set(1,2,3), List(4,5,6), Map('a'->"x",'b'->"y"))
But flatten will not work on following
val l3 = List(1,2,3)
val l4 = List(1,2,3,List('a','b'))
val s1 = "hello world"
val m1 = Map('h'->"h", 'e'->"e", 'l'->"l",'o'->"0")
'flatten' method would create a new list consisting of all elements by removing the hierarchy. Thus it sort of 'flattens' the collection and brings all elements at the same level.
l1.flatten
res0: List[Any] = List(1, 1, 2, -1, 3, 1, -4, 5, a, b)
l2.flatten
res1: List[Any] = List(1, 2, 3, 1, 5, 6, (a,x), (b,y))
2) 'flatMap' first applies a method to elements of a list and then flattens the list. As we noticed above, the flatten method works if lists have a hierarchy (contain other collections). Thus it is important that the method we apply to the elements returns a collection otherwise flatMap will not work
//we have list of lists
val l1 = List(List(1,1,2,-1,3,1,-4,5), List("a","b"))
l1 flatMap(x=>x.toSet)
res2: List[Any] = List(5, 1, -4, 2, 3, -1, a, b)
val l2 = List(Set(1,2,3), List(1,5,6), Map('a'->"x",'b'->"y"))
l2.flatMap(x=>x.toSet)
res3: List[Any] = List(1, 2, 3, 1, 5, 6, (a,x), (b,y))
val s1 = "hello world"
s1.flatMap(x=>Map(x->x.toString))
We notice above that s1.flatten didn't work but s1.flatMap did. This is because, in s1.flatMap, we convert elements of a String (characters) into a Map which is a collection. Thus the string got converted into a collection of Maps like (Map('h'->"h"), Map('e'->"e"), Map('l'->"l"),Map ('l'->"l"),Map('o'->"o")....) Thus flatten will work now. Note that the Map created is not Map('h'->"h", 'e'->"e", 'l'->"l",....).
Take a look at the full signature for flatten:
def flatten[B](implicit asTraversable: (A) ⇒ GenTraversableOnce[B]): List[B]
As you can see, flatten takes an implicit parameter. That parameter provides the rules for how to flatten the given collection types. If the compiler can't find an implicit in scope then it can be provided explicitly.
flatten can flatten almost anything as long as you provide the rules to do so.
Flatmap is basically a map operation followed by a flatten

How does map() on 'zipped' Lists work?

I am looking to calculate the scalar product of two lists. Let's say we have two Lists, l1 = List(1,2,3) and l2 = List(4,5,6), the result should be List(4,10,18)
The code below works:
def scalarProduct(l1 : List[Int], l2 : List[Int]):List[Int] = {
val l3 = l1 zip(l2); l3 map(xy => xy._1*xy._2)
}
However, the following fails to compile, and says Cannot resolve reference map with such signature :
def scalarProduct(l1 : List[Int], l2 : List[Int]):List[Int] = {
val l3 = l1 zip(l2); l3 map((x:Int,y:Int) => x*y)
}
This zip() would return a list of Int pairs, and the above map is also taking a function which takes an Int pair.
Could someone point out why does the second variant fail in this case?
Your second example fails because you provide a function with 2 parameters to the map, while map takes a function with 1 parameter.
Have a look, here's a (simplified) signature of the map function:
def map[B, That](f: A => B): That
The function f is the one that you have to pass to do the conversion. As you can see, it has type A => B, i.e. accept a single parameter.
Now take a look at the (simplified) zip function signature:
def zip [B](that : List[B]) : List[(A, B)]
It actually produces a list whose members are tuples. Tuple of 2 elements looks like this: (A, B). When you call map on the list of tuples, you have to provide the function f that takes a tuple of 2 elements as a parameter, exactly like you do in your first example.
Since it's inconvenient to work with tuples directly, you could extract values of tuple's members to a separate variables using pattern matching.
Here's an REPL session to illustrate this.
scala> List(1, 2, 3)
res0: List[Int] = List(1, 2, 3)
scala> List(2, 3, 4)
res1: List[Int] = List(2, 3, 4)
scala> res0 zip res1
res2: List[(Int, Int)] = List((1,2), (2,3), (3,4))
Here's how you do a standard tuple values extraction with pattern matching:
scala> res2.map(t => t match {
| case (x, y) => x * y
| })
res3: List[Int] = List(2, 6, 12)
It's important to note here that pattern matching expects a partial function as an argument. I.e. the following expression is actually a partial function:
{
case (x, y) => x * y
}
The partial function has its own type in Scala: trait PartialFunction[-A, +B] extends (A) => B, and you could read more about it, for example, here.
Partial function is a normal function, since it extends (A) => B, and that's why you can pass a partial function to the map call:
scala> res2.map { case (x, y) => x * y }
res4: List[Int] = List(2, 6, 12)
You actually use special Scala syntax here, that allows for functions invocations (map in our case) without parentheses around its parameters. You can alternatively write this with parentheses as follows:
scala> res2.map ({ case (x, y) => x * y })
res5: List[Int] = List(2, 6, 12)
There's no difference between the 2 last calls at all.
The fact that you don't have to declare a parameter of anonymous function you pass to the map before you do pattern matching on it, is actually Scala's syntactic sugar. When you call res2.map { case (x, y) => x * y }, what's really going on is pattern matching with partial function.
Hope this helps.
you need something like:
def scalarProduct(l1 : List[Int], l2 : List[Int]):List[Int] = {
val l3 = l1 zip(l2); l3 map{ case (x:Int,y:Int) => x*y}
}
You can have a look at this link to help you with this type of problems.