为什么这个函数参数需要在元组上使用双括号?

时间:2018-03-26 12:40:27

标签: scala scalac

注意:我使用的是 scalac 。请不要建议使用sbt。

我遇到了一个我可以解决的特殊问题,但我想知道它为什么会这样,而不是我以前做过的方式。这是一个代码段:

"indent": [
    true,
    "tabs",
    4
]

它无法编译,导致出现如下错误消息:

def multiply[A](r1: Vector[A], r2: Vector[A], multOp: (A,A) => A, sumOp: (A, A) => A) = 
    r1.zip(r2).map(multOp).reduce(sumOp)

将代码段更改为:

Error:(73, 20) type mismatch;
 found   : (A, A) => A
 required: ((A, A)) => ?
    r1.zip(r2).map(multOp).reduce(sumOp)

将解决问题。

请注意,def multiply[A](r1: Vector[A], r2: Vector[A], multOp: ((A,A)) => A, sumOp: (A, A) => A) = r1.zip(r2).map(multOp).reduce(sumOp) 仅适用于一对大括号。

为什么?

2 个答案:

答案 0 :(得分:3)

方法map定义为采用单个参数,(A, A) => A有两个参数。通过将类型A的两个参数转换为一个参数(类型为(A,A)的元组),它将进行编译。

(A, A) => A // fails due to two params of type A
((A, A)) => A // works due to one param of type (A, A)

另一方面,reduce被定义为采用相同类型的两个参数,因此很高兴采用与该描述匹配的sumOp

以下是TraversableLikeTraversableOnce中的完整签名:

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That

def reduce[A1 >: A](op: (A1, A1) => A1): A1

编辑(额外信息)

原因是reduce始终采用2-arity函数(即两个参数的函数),以便通过在结果上迭代应用该函数将集合减少为单个值以前的申请和下一个价值。另一方面,map总是采用1-arity函数,并将底层值映射到该函数。如果是OptionFuture等,则只有一个单独的基础值,而如果是Vector(与您的一样),可能会有很多,因此它适用它集合到集合的每个元素。

在某些库中,您可能会遇到map2,它具有双参数功能。例如,为了结合两个选项(实际上,任何适用的仿函数,但让理论抛开一边),你可能会这样做:

// presudocode
Option(1, 2).map2((a, b) => a + b)

会给你一个Option(3)。我认为这种机制已被放弃,有利于更容易理解的地图+产品

// presudocode
(Option(1) product Option(2)) map ((a, b) => a + b)

上面一行的实际scalaz语法是(在Scalaz 7中):

(Option(1) |@| Option(2))((a, b) => a + b)

它们同样强大的原则(人们可以做什么,完全相同的另一个人可以做,不多也不少)因此后者通常是首选的,有时它是唯一提供的原则,但是,是的,您可能会不时遇到map2

好的,这是一些额外的信息。至于map,请记住,只有一个参数进来,一个值出来。

答案 1 :(得分:1)

如果您将其定义为:

,则第一个代码段将起作用
def multiply[A](r1: Vector[A], r2: Vector[A], multOp: (A,A) => A, sumOp: (A, A) => A) =
  (r1, r2).zipped.map(multOp).reduce(sumOp)

map元组的zipped方法采用带有两个参数(A, A) => B的函数,因为这是预期的使用模式。

这种方法也避免了中间Vector[(A, A)]的创建。