【问题标题】:Why does this function parameter need double braces on a tuple?为什么这个函数参数在元组上需要双括号?
【发布时间】:2018-03-26 12:40:27
【问题描述】:

注意:我正在使用 scalac。请不要推荐使用 sbt。

我遇到了一个我可以解决的特殊问题,但我想知道为什么它会以这种方式工作,而不是我以前的方式。这是一个代码sn-p:

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)

将 sn-p 更改为:

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

将解决问题。

请注意,sumOp 仅适用于一对大括号。

为什么?

【问题讨论】:

    标签: scala scalac


    【解决方案1】:

    方法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。

    以下是分别在TraversableLike 和TraversableOnce 中找到的完整签名:

    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 元函数(即,两个参数的函数),以便通过迭代地将该函数应用于先前的结果来将集合减少为单个值应用程序和下一个值。另一方面,map 总是采用 1-arity 函数,并将底层值映射到该函数。在Option、Future 等情况下,只有一个基础值,而在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 而言,请记住始终只有一个参数输入和一个值输出。

    【讨论】:

    • 这对我来说确实有意义,即使语法不是很容易解释。谢谢。
    • 是的,Scala 在语法上并不总是超级简单。但我想你会同意这个概念本身是有道理的。另请参阅我编辑的扩展答案,也许它提供了更多有用的信息。干杯!
    【解决方案2】:

    第一个 sn-p 将起作用,如果您将其定义为:

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

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

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

    【讨论】:

      猜你喜欢
      • 2017-01-10
      • 1970-01-01
      • 2020-11-15
      • 1970-01-01
      • 2018-06-24
      • 2017-11-07
      • 2019-11-13
      • 2015-10-03
      • 1970-01-01
      相关资源
      最近更新 更多