【问题标题】:What's the difference between multiple parameters lists and multiple parameters per list in Scala?Scala中的多个参数列表和每个列表的多个参数有什么区别?
【发布时间】:2011-10-11 19:19:44
【问题描述】:

在 Scala 中可以编写(咖喱?)这样的函数

def curriedFunc(arg1: Int) (arg2: String) = { ... }

上面的curriedFunc函数定义有两个参数列表和一个参数列表有多个参数的函数有什么区别:

def curriedFunc(arg1: Int, arg2: String) = { ... }

从数学的角度来看,这是(curriedFunc(x))(y)curriedFunc(x,y),但我可以写def sum(x) (y) = x + y,同样是def sum2(x, y) = x + y

我只知道一个区别 - 这是部分应用的功能。但是这两种方式对我来说都是等价的。

还有其他区别吗?

【问题讨论】:

    标签: scala currying partial-application


    【解决方案1】:

    严格来说,这不是一个柯里化函数,而是一个有多个参数列表的方法,虽然它看起来像一个函数。

    正如您所说,多参数列表允许使用该方法代替部分应用的函数。 (对不起,我使用的通常很愚蠢的例子)

    object NonCurr {
      def tabulate[A](n: Int, fun: Int => A) = IndexedSeq.tabulate(n)(fun)
    }
    
    NonCurr.tabulate[Double](10, _)            // not possible
    val x = IndexedSeq.tabulate[Double](10) _  // possible. x is Function1 now
    x(math.exp(_))                             // complete the application
    

    另一个好处是,如果第二个参数列表由单个函数或 thunk 组成,您可以使用花括号代替括号,这看起来不错。例如

    NonCurr.tabulate(10, { i => val j = util.Random.nextInt(i + 1); i - i % 2 })
    

    IndexedSeq.tabulate(10) { i =>
      val j = util.Random.nextInt(i + 1)
      i - i % 2
    }
    

    或者对于thunk:

    IndexedSeq.fill(10) {
      println("debug: operating the random number generator")
      util.Random.nextInt(99)
    }
    

    另一个优点是,您可以参考先前参数列表的参数来定义默认参数值(尽管您也可以说在单个列表中不能这样做是一个缺点:)

    // again I'm not very creative with the example, so forgive me
    def doSomething(f: java.io.File)(modDate: Long = f.lastModified) = ???
    

    最后,在相关帖子Why does Scala provide both multiple parameters lists and multiple parameters per list? 的回复中还有其他三个应用程序。我将在这里复制它们,但功劳归于 Knut Arne Vedaa、Kevin Wright 和即兴的。

    首先:你可以有多个 var args:

    def foo(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum
    

    ...这在单个参数列表中是不可能的。

    第二,它有助于类型推断:

    def foo[T](a: T, b: T)(op: (T,T) => T) = op(a, b)
    foo(1, 2){_ + _}   // compiler can infer the type of the op function
    
    def foo2[T](a: T, b: T, op: (T,T) => T) = op(a, b)
    foo2(1, 2, _ + _)  // compiler too stupid, unfortunately
    

    最后,这是获得隐式和非隐式参数的唯一方法,因为implicit 是整个参数列表的修饰符:

    def gaga [A](x: A)(implicit mf: Manifest[A]) = ???   // ok
    def gaga2[A](x: A, implicit mf: Manifest[A]) = ???   // not possible
    

    【讨论】:

    • 因为它是投票最多的答案,我认为问题的标题不再与其答案相对应。我认为应该将标题更改为“为什么 Scala 同时提供多个参数列表和每个列表的多个参数?”,即它已经被示例与 stackoverflow.com/questions/4684185/… 合并。
    【解决方案2】:

    0__ 的优秀answer 没有涵盖另一个差异:默认参数。当计算另一个参数列表中的默认值时,可以使用一个参数列表中的参数,但不能在同一个参数列表中使用。

    例如:

    def f(x: Int, y: Int = x * 2) = x + y // not valid
    def g(x: Int)(y: Int = x * 2) = x + y // valid
    

    【讨论】:

    • 以这个简单的例子来理解这一点。这实际上使默认参数更加有用。谢谢!
    • 很好的例子,除了我花了五分钟弄清楚如何调用它:g(1)() 返回 3。g(1)(2) 返回 5。
    【解决方案3】:

    这就是重点,咖喱和非咖喱形式是等价的!正如其他人所指出的那样,根据具体情况,一种或另一种形式在句法上可能更方便使用,这是首选其中一种形式的唯一原因。

    重要的是要理解,即使 Scala 没有用于声明柯里化函数的特殊语法,您仍然可以构造它们;一旦您有能力创建返回函数的函数,这只是数学上的必然性。

    为了证明这一点,假设def foo(a)(b)(c) = {...} 语法不存在。然后你仍然可以实现完全相同的东西:def foo(a) = (b) => (c) => {...}

    与 Scala 中的许多功能一样,这只是一种语法上的便利,用于做一些无论如何都是可能的事情,但稍微有点冗长。

    【讨论】:

      【解决方案4】:

      这两种形式是同构的。主要区别在于柯里化函数更容易部分应用,而非柯里化函数的语法稍好一些,至少在 Scala 中是这样。

      【讨论】:

      • 前面不是说过这些例子是不是柯里化函数吗?我知道咖喱函数只有一个参数,并且可能返回一个带有单个参数的函数,依此类推,直到有一个所有参数都关闭的主体。我错了吗?
      猜你喜欢
      • 1970-01-01
      • 2020-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-12
      • 2020-01-09
      • 1970-01-01
      相关资源
      最近更新 更多