'zipped' 列表上的 map() 如何工作?

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

我想计算两个列表的标量积。假设我们有两个列表,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() 将 return 一个 Int 对列表,上面的映射也采用了一个采用 Int 对的函数。 有人能指出为什么第二种变体在这种情况下会失败吗?

你需要这样的东西:

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 以帮助您解决此类问题。

您的第二个示例失败,因为您向 map 提供了一个具有 2 个参数的函数,而 map 接受了一个具有 1 个参数的函数。

看看,这是 map 函数的(简化)签名:

def map[B, That](f: A => B): That

函数 f 是您必须传递以进行转换的函数。如您所见,它的类型为 A => B,即接受单个参数。

现在看看(简化的)zip 函数签名:

def zip [B](that : List[B]) : List[(A, B)]

它实际上产生了一个列表,其成员是元组。 2 个元素的元组如下所示:(A, B)。当您在元组列表上调用 map 时,您必须提供函数 f,它将 2 个元素的元组作为参数,就像您在第一个示例中所做的那样。

由于直接使用元组不方便,您可以使用模式匹配将元组成员的值提取到单独的变量中。

这里有一个 REPL 会话来说明这一点。

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))

以下是使用模式匹配进行标准元组值提取的方法:

scala> res2.map(t => t match {
     |   case (x, y) => x * y
     | })
res3: List[Int] = List(2, 6, 12)

这里需要注意的是,模式匹配需要部分函数作为参数。 IE。下面的表达式实际上是一个偏函数:

{
    case (x, y) => x * y
}

部分函数在 Scala 中有自己的类型:trait PartialFunction[-A, +B] extends (A) => B,您可以阅读更多相关信息,例如 here

偏函数是一个普通函数,因为它扩展了 (A) => B,这就是为什么您可以将偏函数传递给 map 调用:

scala> res2.map { case (x, y) => x * y }
res4: List[Int] = List(2, 6, 12)

您实际上在这里使用了特殊的 Scala 语法,它允许函数调用(在我们的例子中为 map),其参数周围没有括号。你也可以用括号写成如下:

scala> res2.map ({ case (x, y) => x * y })
res5: List[Int] = List(2, 6, 12)

最后两次通话完全没有区别。

在对 map 进行模式匹配之前,您不必声明传递给 map 的匿名函数的参数,这实际上是 Scala 的语法糖。当您调用 res2.map { case (x, y) => x * y } 时,真正发生的是与部分函数的模式匹配。

希望对您有所帮助。