6.3 具有多参数的函数值

我们能够定义和使用具有多个参数的函数值。下面的inject()方法是一个例子,它对一个Int数组的一个元素进行操作,将结果传给对下一个元素的操作。这样,就可以把对每个元素操作的结果进行级联或迭加。

  1. def inject(arr: Array[Int], initial: Int, operation: (Int, Int) => Int) :
  2. Int = {
  3. var carryOver = initial
  4. arr.foreach(element => carryOver = operation(carryOver, element) )
  5. carryOver
  6. }

inject()方法有三个参数,Int数组、注入操作的初始Int值和作为函数值的操作本身。在方法里,将变量carryOver设为初始值,然后用foreach()方法对给定数组的元素进行循环。这个方法接收一个函数值作为参数,用数组的每个元素作为实参调用这个函数值。在传给foreach()做实参的函数里,用两个实参调用了给定的操作:carryOver的值以及在这个上下文里的element。调用这个操作的结果会赋给变量carryOver,这样一来,就可以把它作为实参传给随后对操作的调用。等完成了对数组每个元素的操作调用之后,就把carryOver的最终值返回。

我们来看一些使用上面inject()方法的例子。下面是如何对数组元素求和:

  1. val array = Array(2, 3, 5, 1, 6, 4)
  2. val sum = inject(array, 0, (carryOver, elem) => carryOver + elem)
  3. println("Sum of elements in array " + array.toString() + " is " + sum)

inject()方法的第一个实参是一个数组,也就是想要求和的所有元素。第二个实参是求和的初始值0。第三个实参是执行元素求和操作的函数,一次一个。

如果不是要求和,而是要找到最大值,同样可以使用inject()方法:

  1. val max = inject(array, Integer.MIN_VALUE,
  2. (carryOver, elem) => Math.max(carryOver, elem)
  3. )
  4. println("Max of elements in array " + array.toString() + " is " + max)

执行上面两个对inject()方法调用,输出如下:

  1. Sum of elements in array Array(2, 3, 5, 1, 6, 4) is 21
  2. Max of elements in array Array(2, 3, 5, 1, 6, 4) is 6

如果想要遍历容器里的元素,执行一些操作,我们不必真的去编写自己的inject()——我写它只是为了演示。Scala程序库已经内建了这个方法。它就是foldLeft(),也是/:方法③。下面是个例子,演示了用它对数组元素求和和求最大值。

③参见8.4节“方法名约定”,了解以冒号(:)结尾的方法。

  1. val array = Array(2, 3, 5, 1, 6, 4)
  2. val sum = (0 /: array) { (sum, elem) => sum + elem }
  3. val max = (Integer.MIN_VALUE /: array) {
  4. (large, elem) => Math.max(large, elem) }
  5. println("Sum of elements in array " + array.toString() + " is" + sum)
  6. println("Max of elements in array " + array.toString() + " is" + max)

作为一个有敏锐观察力的读者,你或许注意到了,函数值放到了大括号里面,而不是作为实参传给函数。这样比用括号包着作为实参传入要好看得多。不过,如果尝试像下面这样调用inject()方法,就会出现错误:

FunctionValuesAndClosures/Inject3.scala

  1. val sum = inject(array, 0) {(carryOver, elem) => carryOver + elem}

上面的代码会导致如下的错误:

  1. (fragment of Inject3.scala):11: error: wrong number of arguments for
  2. method inject: (Array[Int],Int,(Int, Int) => Int)Int
  3. val sum = inject(array, 0) {(carryOver, elem) => carryOver + elem}
  4. ^
  5. one error found
  6. !!!
  7. discarding <script preamble>

这可不是我们想看到的。在享受同程序库方法所享用的相同益处之前,我们必须了解另一个概念——curry化。