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

den*_*dym 78 scala currying partial-application

在Scala中,可以像这样编写(curried?)函数

def curriedFunc(arg1: Int) (arg2: String) = { ... }
Run Code Online (Sandbox Code Playgroud)

上述curriedFunc函数定义与两个参数列表和单个参数列表中具有多个参数的函数之间有何区别:

def curriedFunc(arg1: Int, arg2: String) = { ... }
Run Code Online (Sandbox Code Playgroud)

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

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

还有其他差异吗?

0__*_*0__ 85

严格地说,这不是一个curried函数,而是一个带有多个参数列表的方法,尽管它看起来像一个函数.

如上所述,多个参数列表允许该方法用于部分应用函数的位置.(对不起我使用的一般愚蠢的例子)

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
Run Code Online (Sandbox Code Playgroud)

另一个好处是你可以使用花括号而不是括号,如果第二个参数列表由单个函数或thunk组成,那么它看起来很好.例如

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

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

或者对于thunk:

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

另一个优点是,您可以引用前一个参数列表的参数来定义默认参数值(尽管您也可以说它是一个缺点,您不能在单个列表中执行此操作:)

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

最后,在相关帖子的答案中有三个其他应用程序为什么Scala提供多个参数列表和每个列表的多个参数?.我会在这里复制它们,但是归功于Knut Arne Vedaa,Kevin Wright和临时演员.

第一:你可以拥有多个var args:

def foo(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum
Run Code Online (Sandbox Code Playgroud)

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

其次,它有助于类型推断:

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
Run Code Online (Sandbox Code Playgroud)

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

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

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

Dan*_*ral 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
Run Code Online (Sandbox Code Playgroud)


Tom*_*ett 19

这就是重点,就是那些咖喱和无条件的形式是等价的!正如其他人所指出的那样,根据情况,一种或另一种形式在语法上可以更方便地使用,这是优先选择另一种形式的唯一原因.

重要的是要理解即使Scala没有用于声明curried函数的特殊语法,你仍然可以构造它们; 一旦你能够创建返回函数的函数,这只是一个数学必然性.

为了证明这一点,假设def foo(a)(b)(c) = {...}语法不存在.然后你仍然可以完成同样的事情:def foo(a) = (b) => (c) => {...}.

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