80

多个参数列表,例如def foo(a:Int)(b:Int) = {}每个列表的多个参数,例如,def foo(a:Int, b:Int) = {}据我所知,在语义上是等效的,并且大多数函数式语言只有一种声明多个参数的方式,例如 F#。

我能弄清楚支持这两种函数定义风格的唯一原因是允许使用只有一个参数的参数列表进行类似语法的语言扩展。

def withBufferedWriter(file: File)(block: BufferedWriter => Unit)

现在可以使用语法外观调用

withBufferedWriter(new File("myfile.txt")) { out =>
  out write "whatever"
  ...
}

但是,可以有其他方法来支持使用花括号,而无需多个参数列表。

一个相关的问题:为什么在 Scala 中使用多个参数列表称为“currying”?柯里化通常被定义为一种为了支持部分应用而使 n 元函数一元的技术。但是,在 Scala 中,可以部分应用一个函数,而无需制作该函数的“curried”(多个参数列表,每个参数列表,每个参数列表)版本。

4

5 回答 5

78

它使您能够执行以下操作:

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

scala> foo(1, 2, 3)(4, 5, 6, 7, 9)(10, 11)
res7: Int = 3906
于 2011-01-13T19:55:43.067 回答
49

除了允许您编写看起来像语言一部分的方法(您已经发现)之外,值得注意的是类型推断器一次只能处理一个块。

所以在这个:

def foo[T](a: T, b: T)(op: (T,T)=>T) = op(a,b)
foo(1,2){_+_}

T将首先推断为Int,然后将其用作闭包中两个下划线的类型。这就是编译器如何以完全类型安全的方式知道 + 操作是有效的。

于 2011-01-14T00:36:01.140 回答
29

要回答您的“相关问题”,currying 只是一种将多个参数的函数(例如(A, B, C) => D)转换为接受一个参数并返回一个函数的函数的方法,例如A => (B => (C => D))(括号显示但不是必需的)。

元组化形式和柯里化形式是同构的,我们可以在它们之间自由转换。所有这些都是等价的,但具有不同的语法含义:

(A, B, C, D, E) => F
((A, B), (C, D, E)) => F
(A, B) => (C, D, E) => F

当您声明单独的参数组时,这就是您正在做的那种柯里化。多参数组方法是一种返回函数的方法......你可以在 REPL 中看到:

scala> def foo(a:Int, b:Int)(c:Int, d:Int, e:Int):Int = 9
foo: (a: Int,b: Int)(c: Int,d: Int,e: Int)Int

scala> foo _                                             
res4: (Int, Int) => (Int, Int, Int) => Int = <function2>
于 2011-01-13T21:36:01.173 回答
22

默认参数中的反向引用:

case class Foo(bar: Int)

def test(f: Foo)(i: Int = f.bar) = i*i

test(Foo(3))()
于 2013-02-27T20:22:33.927 回答
15

我知道其中一个动机是隐式参数列表。“隐式”是列表的属性,而不是参数。另一个可能是案例类:只有第一个参数列表成为案例字段。

于 2011-01-14T05:36:25.447 回答