Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 552
Ankit Khettry Avatar asked Mar 02 '16 10:03

Ankit Khettry


People also ask

What is map and zip in Python?

zip() in PythonIt is used to map the similar index of multiple containers so that they can be used just using a single entity. Syntax : zip(*iterators) Parameters : Python iterables or containers ( list, string etc ) Return Value : Returns a single iterator object, having mapped values from all the. containers.

Can you zip more than two lists Python?

The Python zip() function makes it easy to also zip more than two lists. This works exactly like you'd expect, meaning you just only need to pass in the lists as different arguments.

Does zip return a tuple?

The zip() function takes iterables (can be zero or more), aggregates them in a tuple, and returns it.

How does zip work in Scala?

The zip function is applicable to both Scala's Mutable and Immutable collection data structures. The zip method takes another collection as parameter and will merge its elements with the elements of the current collection to create a new collection consisting of pairs or Tuple2 elements from both collections.


Video Answer


2 Answers

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.

like image 177
Haspemulator Avatar answered Oct 18 '22 18:10

Haspemulator


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.

like image 22
Carlos Vilchez Avatar answered Oct 18 '22 18:10

Carlos Vilchez