"zipped"列表中的map()如何工作?

Ank*_*try 6 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)
}
Run Code Online (Sandbox Code Playgroud)

但是,以下内容无法编译,并说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)
}
Run Code Online (Sandbox Code Playgroud)

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

Has*_*tor 16

您的第二个示例失败,因为您提供了一个带有2个参数的函数map,而map带有1个参数的函数.

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

def map[B, That](f: A => B): That
Run Code Online (Sandbox Code Playgroud)

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

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

def zip [B](that : List[B]) : List[(A, B)]
Run Code Online (Sandbox Code Playgroud)

它实际上产生一个列表,其成员是元组.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))
Run Code Online (Sandbox Code Playgroud)

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

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

这里需要注意的是,模式匹配需要将部分函数作为参数.即以下表达式实际上是部分函数:

{
    case (x, y) => x * y
}
Run Code Online (Sandbox Code Playgroud)

部分函数在Scala中有自己的类型:trait PartialFunction[-A, +B] extends (A) => B你可以在这里阅读更多关于它的信息.

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

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

实际上你在这里使用了特殊的Scala语法,它允许函数调用(map在我们的例子中),而不包括围绕其参数的括号.您也可以用括号写下这个,如下所示:

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

两个最后一次通话之间没有区别.

事实上,你不必声明你传递给map模式匹配之前的匿名函数的参数,实际上是Scala的语法糖.当你打电话时res2.map { case (x, y) => x * y },真正发生的是与部分功能的模式匹配.

希望这可以帮助.