scala> def a(i:Int)(j:Int) = i * j
a: (i: Int)(j: Int)Int
scala> def b(i:Int, j:Int) = i * j
b: (i: Int, j: Int)Int
这两个定义非常相似,它们(在我看来)做同样的事情。
除了定义一个接收隐式参数或代码块作为参数的函数之外,还有什么理由使用第一种定义样式?
scala> def a(i:Int)(j:Int) = i * j
a: (i: Int)(j: Int)Int
scala> def b(i:Int, j:Int) = i * j
b: (i: Int, j: Int)Int
这两个定义非常相似,它们(在我看来)做同样的事情。
除了定义一个接收隐式参数或代码块作为参数的函数之外,还有什么理由使用第一种定义样式?
这是我长期以来编制的清单:
1)跨多个参数列表的类型解析
class ResourceManager {
type Resource
def open: Resource = ???
}
class ResourceManagerTest {
// Does not compile: def test1(rm: ResourceManager, r: rm.Resource) = ???
// Compiles: This way the type can be resolved
def test2(rm: ResourceManager)(r: rm.Resource) = ???
}
2)类型推断,其中较早的参数可以为以后的参数“锁定”类型参数(感谢 Myserious Dan)
def foo1[A](x: A, f: A => Int) = ???
def foo2[A](x: A)(f: A => Int) = ???
def foo1foo2Demo() {
// This will always demand a type annotation on any anonymous function
// you pass in:
foo1(1, (i: Int) => i * i)
// Does not compile: foo1(1, i => i * i)
// Type not required
foo2(2)(i => i * i)
}
3)类语法语言扩展
object MultipleArgumentListsDemo {
// This style of function definition allows syntax-like language extensions
@tailrec
def myWhile(conditional: => Boolean)(f: => Unit) {
if (conditional) {
f
myWhile(conditional)(f)
}
}
def myWhileDemo() {
var count = 0
myWhile(count < 5) {
count += 1
println(count)
}
}
4)同时具有隐式和非隐式参数,因为隐式是整个参数列表的修饰符:
def f[A](x: A)(implicit mf: Manifest[A]) {
}
5) 一个参数列表中的参数值可用于计算另一个参数列表中的默认值,但不能用于计算同一个参数列表中的默认值。
def g(x: Int)(y: Int = x * 2) = {
x + y
}
6)多个重复的参数列表(“varargs”)
def h(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum
7)部分应用
def i() {
val foop = h(1, 2, 3)(4, 5, 6, 7, 9) _
println(foop(Seq(10, 11)))
}
由于我在编译该列表时没有跟踪我的来源:部分或所有示例可能是从其他地方复制的(关于 SO 的其他问题),所以请记下,我将添加关于哪里的参考它来自。
以这种方式“currying”函数的主要原因是启用部分应用程序:
scala> val c = a(5) _
c: Int => Int = <function1>
这里 c 是一个函数,它接受单个 int 并返回将该 int 与 5 相乘的结果。您可能会在一个方法中设置 c,然后将其传递给另一个方法,该方法期望函数接受一个 Int 参数。在这种情况下有点微不足道,但对于一系列用途来说非常灵活。
除了支持柯里化之外,它还有助于类型推断:如果所有内容都在一个参数列表中,有时编译器无法推断出正确的类型,但如果您拆分依赖于其他参数绑定的部分,它就可以工作。一个典型的例子是foldLeft
:尝试用一个参数列表来实现它,然后在某些情况下编译器需要类型注释。