6.3 具有多参数的函数值
我们能够定义和使用具有多个参数的函数值。下面的inject()
方法是一个例子,它对一个Int
数组的一个元素进行操作,将结果传给对下一个元素的操作。这样,就可以把对每个元素操作的结果进行级联或迭加。
def inject(arr: Array[Int], initial: Int, operation: (Int, Int) => Int) :
Int = {
var carryOver = initial
arr.foreach(element => carryOver = operation(carryOver, element) )
carryOver
}
inject()
方法有三个参数,Int
数组、注入操作的初始Int
值和作为函数值的操作本身。在方法里,将变量carryOver
设为初始值,然后用foreach()
方法对给定数组的元素进行循环。这个方法接收一个函数值作为参数,用数组的每个元素作为实参调用这个函数值。在传给foreach()
做实参的函数里,用两个实参调用了给定的操作:carryOver
的值以及在这个上下文里的element
。调用这个操作的结果会赋给变量carryOver
,这样一来,就可以把它作为实参传给随后对操作的调用。等完成了对数组每个元素的操作调用之后,就把carryOver
的最终值返回。
我们来看一些使用上面inject()
方法的例子。下面是如何对数组元素求和:
val array = Array(2, 3, 5, 1, 6, 4)
val sum = inject(array, 0, (carryOver, elem) => carryOver + elem)
println("Sum of elements in array " + array.toString() + " is " + sum)
inject()
方法的第一个实参是一个数组,也就是想要求和的所有元素。第二个实参是求和的初始值0。第三个实参是执行元素求和操作的函数,一次一个。
如果不是要求和,而是要找到最大值,同样可以使用inject()
方法:
val max = inject(array, Integer.MIN_VALUE,
(carryOver, elem) => Math.max(carryOver, elem)
)
println("Max of elements in array " + array.toString() + " is " + max)
执行上面两个对inject()
方法调用,输出如下:
Sum of elements in array Array(2, 3, 5, 1, 6, 4) is 21
Max of elements in array Array(2, 3, 5, 1, 6, 4) is 6
如果想要遍历容器里的元素,执行一些操作,我们不必真的去编写自己的inject()
——我写它只是为了演示。Scala程序库已经内建了这个方法。它就是foldLeft()
,也是/:方法③。下面是个例子,演示了用它对数组元素求和和求最大值。
③参见8.4节“方法名约定”,了解以冒号(
:
)结尾的方法。
val array = Array(2, 3, 5, 1, 6, 4)
val sum = (0 /: array) { (sum, elem) => sum + elem }
val max = (Integer.MIN_VALUE /: array) {
(large, elem) => Math.max(large, elem) }
println("Sum of elements in array " + array.toString() + " is" + sum)
println("Max of elements in array " + array.toString() + " is" + max)
作为一个有敏锐观察力的读者,你或许注意到了,函数值放到了大括号里面,而不是作为实参传给函数。这样比用括号包着作为实参传入要好看得多。不过,如果尝试像下面这样调用inject()
方法,就会出现错误:
FunctionValuesAndClosures/Inject3.scala
val sum = inject(array, 0) {(carryOver, elem) => carryOver + elem}
上面的代码会导致如下的错误:
(fragment of Inject3.scala):11: error: wrong number of arguments for
method inject: (Array[Int],Int,(Int, Int) => Int)Int
val sum = inject(array, 0) {(carryOver, elem) => carryOver + elem}
^
one error found
!!!
discarding <script preamble>
这可不是我们想看到的。在享受同程序库方法所享用的相同益处之前,我们必须了解另一个概念——curry化。