map()如何'压缩'列表有效吗?

时间:2016-03-02 10:16:21

标签: scala scala-collections

我希望计算两个列表的标量积。我们假设我们有两个列表l1 = List(1,2,3)l2 = List(4,5,6),结果应为List(4,10,18)

以下代码有效:

def scalarProduct(l1 : List[Int], l2 : List[Int]):List[Int] = {
  val l3 = l1 zip(l2); l3 map(xy => xy._1*xy._2)
}

但是,以下内容无法编译,并说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)
}

这个zip()将返回一个Int对列表,上面的映射也采用了一个Int对的函数。 有人可以指出为什么第二种变体在这种情况下失败了吗?

2 个答案:

答案 0 :(得分:15)

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.

答案 1 :(得分:1)

你需要这样的东西:

def scalarProduct(l1 : List[Int], l2 : List[Int]):List[Int] = {
  val l3 = l1 zip(l2); l3 map{ case (x:Int,y:Int) => x*y}
}

您可以查看this link来帮助解决此类问题。