方法可能有多个参数列表。
示例
以下是在 Scala 集合 API 中 Iterable
特征中定义的一个示例
trait Iterable[A] {
...
def foldLeft[B](z: B)(op: (B, A) => B): B
...
}
trait Iterable[A]:
...
def foldLeft[B](z: B)(op: (B, A) => B): B
...
foldLeft
将一个双参数函数 op
应用于一个初始值 z
和此集合的所有元素,从左到右。下面显示了其用法的一个示例。
从初始值 0 开始,foldLeft
在此将函数 (m, n) => m + n
应用于列表中的每个元素和先前的累积值。
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val res = numbers.foldLeft(0)((m, n) => m + n)
println(res) // 55
用例
多参数列表的建议用例包括
驱动类型推断
碰巧在 Scala 中,类型推断一次进行一个参数列表。假设您有以下方法
def foldLeft1[A, B](as: List[A], b0: B, op: (B, A) => B) = ???
然后您希望以以下方式调用它,但会发现它无法编译
def notPossible = foldLeft1(numbers, 0, _ + _)
您必须以以下方式之一调用它
def firstWay = foldLeft1[Int, Int](numbers, 0, _ + _)
def secondWay = foldLeft1(numbers, 0, (a: Int, b: Int) => a + b)
这是因为 Scala 无法推断函数 _ + _
的类型,因为它仍在推断 A
和 B
。通过将参数 op
移动到其自己的参数列表,A
和 B
在第一个参数列表中被推断出来。这些推断出的类型随后将可用于第二个参数列表,并且 _ + _
将匹配推断出的类型 (Int, Int) => Int
def foldLeft2[A, B](as: List[A], b0: B)(op: (B, A) => B) = ???
def possible = foldLeft2(numbers, 0)(_ + _)
此定义不需要任何类型提示,并且可以推断其所有类型参数。
隐式参数
若要仅将某些参数指定为 implicit
,则必须将它们放在其自己的 implicit
参数列表中。
一个示例是
def execute(arg: Int)(implicit ec: scala.concurrent.ExecutionContext) = ???
def execute(arg: Int)(using ec: scala.concurrent.ExecutionContext) = ???
部分应用
当使用较少数量的参数列表调用方法时,这将产生一个将缺少的参数列表作为其参数的函数。这在形式上称为 部分应用。
例如,
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val numberFunc = numbers.foldLeft(List[Int]()) _
val squares = numberFunc((xs, x) => xs :+ x*x)
println(squares) // List(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)
val cubes = numberFunc((xs, x) => xs :+ x*x*x)
println(cubes) // List(1, 8, 27, 64, 125, 216, 343, 512, 729, 1000)
与“柯里化”的比较
有时您可能会看到一个具有多个参数列表的方法被称为“柯里化”。
正如 维基百科关于柯里化的文章 所述,
柯里化是一种将采用多个参数的函数转换为一系列每个函数采用单个参数的函数的技术
出于两个原因,我们不鼓励使用单词“curry”来引用 Scala 的多个参数列表
1) 在 Scala 中,多重参数和多重参数列表被直接指定并实现,作为语言的一部分,而不是从单参数函数派生而来。
2) 可能会与 Scala 标准库的 curried
和 uncurried
方法混淆,它们根本不涉及多重参数列表。
无论如何,在多重参数列表和柯里化之间肯定存在相似之处。尽管它们在定义站点不同,但调用站点可能看起来相同,如下例所示
// version with multiple parameter lists
def addMultiple(n1: Int)(n2: Int) = n1 + n2
// two different ways of arriving at a curried version instead
def add(n1: Int, n2: Int) = n1 + n2
val addCurried1 = (add _).curried
val addCurried2 = (n1: Int) => (n2: Int) => n1 + n2
// regardless, all three call sites are identical
addMultiple(3)(4) // 7
addCurried1(3)(4) // 7
addCurried2(3)(4) // 7