Scala中的多个参数列表和每个列表的多个参数之间有什么区别?


81

在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

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

还有其他区别吗?

Answers:


88

严格来说,这不是一个咖喱函数,而是一个具有多个参数列表的方法,尽管它看起来像一个函数。

如您所说,多个参数列表允许使用该方法代替部分应用的函数。(很抱歉我使用的愚蠢示例)

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
}

或为重击:

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) = ???

最后,在相关文章的答案中还有其他三个应用程序。为什么Scala同时提供多个参数列表和每个列表多个参数?。我将在这里复制它们,但要感谢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

最后,这是拥有隐式和非隐式args的唯一方法,这是implicit整个参数列表的修饰符:

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

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

42

0__的出色答案未涵盖另一个差异:默认参数。计算另一个参数列表(而不是同一参数列表)中的默认值时,可以使用一个参数列表中的参数。

例如:

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

为此,我们花了一个简单的示例。这实际上使默认参数更加有用。谢谢!
Mike McFarland

很好的例子,但我花了五分钟弄清楚如何称呼它:g(1)()返回3. g(1)(2)返回5
贤明

19

重点在于,咖喱形式和非咖喱形式是等效的!正如其他人指出的那样,根据情况,使用一种或另一种形式在语法上可能更方便,这是优先选择一种形式而不是另一种形式的唯一原因。

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

为了说明这一点,请想象该def foo(a)(b)(c) = {...}语法不存在。然后,您仍然可以实现完全相同的效果,例如:def foo(a) = (b) => (c) => {...}

像Scala中的许多功能一样,这只是语法上的便利,可以做一些可能会做的事情,但是要稍微冗长一些。


4

这两种形式是同构的。主要区别在于,咖喱函数更易于部分应用,而非咖喱函数的语法稍好,至少在Scala中。


2
难道不是前面说过的例子不是咖喱函数吗?我知道咖喱函数只有一个参数,并且可能会返回一个带有单个参数的函数,依此类推,直到有一个所有参数都被封闭的物体为止。我错了吗?
Jacek Laskowski
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.