【问题标题】:scala currying by nested functions or by multiple parameter lists通过嵌套函数或多个参数列表进行 scala currying
【发布时间】:2011-01-15 00:53:28
【问题描述】:

在 Scala 中,我可以定义一个带有两个参数列表的函数。

def myAdd(x :Int)(y :Int) = x + y

这使得定义部分应用的函数变得容易。

val plusFive = myAdd(5) _

但是,我可以通过定义和返回嵌套函数来完成类似的事情。

  def myOtherAdd(x :Int) = {
    def f(y :Int) = x + y
    f _
  }

在外观上,我已经移动了下划线,但这仍然感觉像在柯里化。

val otherPlusFive = myOtherAdd(5)

我应该使用什么标准来选择一种方法而不是另一种方法?

【问题讨论】:

    标签: scala


    【解决方案1】:

    至少有四种方法可以完成同一件事:

    def myAddA(x: Int, y: Int) = x + y
    val plusFiveA: Int => Int = myAddA(5,_)
    
    def myAddB(x: Int)(y : Int) = x + y
    val plusFiveB = myAddB(5) _
    
    def myAddC(x: Int) = (y: Int) => x + y
    val plusFiveC = myAddC(5)
    
    def myAddD(x: Int) = {
      def innerD(y: Int) = x + y
      innerD _
    }
    val plusFiveD = myAddD(5)
    

    您可能想知道哪种风格最有效哪种风格最好(对于一些非基于性能的最佳衡量标准)。

    就效率而言,事实证明这四个本质上是等效的。前两种情况实际上发出完全相同的字节码; JVM 对多个参数列表一无所知,因此一旦编译器弄清楚(您需要在案例 A 上使用类型注释来帮助它),它在引擎盖下都是一样的。第三种情况也非常接近,但是由于它预先承诺返回一个函数并在现场指定它,它可以避免一个内部字段。就完成的工作而言,第四种情况与前两种情况几乎相同;它只是在方法内部而不是外部转换为Function1

    就风格而言,我建议 B 和 C 是最好的选择,这取决于你在做什么。如果您的主要用例是创建一个函数,而不是使用两个参数列表就地调用,那么请使用 C,因为它会告诉您它将做什么。 (例如,这个版本对于来自 Haskell 的人来说也特别熟悉。)另一方面,如果你主要是在原地调用它,但只是偶尔 curry 它,那么使用 B。同样,它更清楚地说明了什么预计会这样做。

    【讨论】:

    • 谢谢,雷克斯。我不知道方法 A 和 C。你的回答真的很有帮助。
    【解决方案2】:

    你也可以这样做:

    def yetAnotherAdd(x: Int) = x + (_: Int)
    

    您应该根据意图选择 API。 Scala 中有多个参数列表的主要原因是为了帮助类型推断。例如:

    def f[A](x: A)(f: A => A) = ...
    f(5)(_ + 5)
    

    也可以使用它来拥有多个可变参数,但我从未见过这样的代码。当然,还有隐式参数列表的需要,但这几乎是另一回事了。

    现在,有很多方法可以让函数返回函数,这几乎就是 currying 所做的。如果 API应该被认为是一个返回函数的函数,你应该使用它们。

    我认为很难比这更精确。

    【讨论】:

    • 谢谢,丹尼尔。您关于类型推断作为多个参数列表的动机的评论令人大开眼界。这真的很有帮助。
    • 另一个动机是编写可以很好地集成到语言中的 DSL,例如 using(x) { ... }。诚然,这确实也利用了为多个块提供的类型推断。
    【解决方案3】:

    让方法直接返回函数(而不是使用部分应用程序)的另一个好处是,在使用中缀表示法时,它可以使代码更简洁,从而避免在更复杂的表达式中出现大量括号和下划线。

    考虑:

    val list = List(1,2,3,4)
    
    def add1(a: Int)(b: Int) = a + b
    list map { add1(5) _ }    
    
    //versus
    
    def add2(a: Int) = a + (_: Int)
    list map add2(5)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-27
      相关资源
      最近更新 更多