11

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?

Ankit Khettry
  • 997
  • 1
  • 13
  • 33

2 Answers2

20

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.

Haspemulator
  • 11,050
  • 9
  • 49
  • 76
1

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.

Carlos Vilchez
  • 2,774
  • 28
  • 30
  • when you use a `map` with a list, you have a reference to each object of the list. So you would need pattern matching to transform that reference into an object you may use. For those cases, instead of using `map(x => x._1 * x._2)` you should use `map{x => x match { case (x:Int,y:Int) => x*y}` As that is quite ugly, we can use some syntactic sugar: `map{ case (x:Int,y:Int) => x*y}` – Carlos Vilchez Mar 02 '16 at 10:26
  • It's pretty strange, since one of these definitions has `map(f)` and another has `map{f}`. I hate to admit but I am totally confused now – Ankit Khettry Mar 02 '16 at 10:32
  • map() is a method that accepts a function. In the answer, I am unable to understand whether we are actually passing a function as argument to map(), or are we redefining the map() method itself – Ankit Khettry Mar 02 '16 at 10:34
  • When we use parentheses is because inside we are defining an expression. When we use curly braces we are defining a block of code. – Carlos Vilchez Mar 02 '16 at 10:42
  • 1
    And about your last comment, we are passing a function to map. In fact we are defining that function. – Carlos Vilchez Mar 02 '16 at 10:44