6.4 Curry化
Scala里的curry化可以把函数从接收多个参数转换成接收多个参数列表。如果要用同样的一组实参多次调用同一个函数,可以用curry化来减少噪音,让代码更有味道。
我们来看看Scala如何提供curry化的支持。我们要编写的方法不是接收一个参数列表,里面有多个参数,而是有多个参数列表,每个里面只有一个参数。(每个参数列表也可以有多个参数。)也就是说,写的不是def foo(a: Int, b: Int, c: Int) {}
,而是def foo(a: Int)(b: Int)(c: Int) {}
。可以这样调用这个方法,比如,foo(1)(2)(3)
、foo(1){2}{3}
,甚至这样foo{1}{2}{3}
。
我们来考察一下定义成有多个参数列表的方法会怎么样。看看下面这段Scala交互式shell的会话:
scala> def foo(a: Int)(b: Int)(c:Int) {}
foo: (Int)(Int)(Int)Unit
scala> foo _
res1: (Int) => (Int) => (Int) => Unit = <function>
scala>
按照上面的讨论,我们定义了一个函数foo()
。然后,调用foo_
创建了一个偏应用函数(见6.8节“偏应用函数”),也就是说,这个函数有一个或多个参数未绑定。创建出的偏应用函数可以赋给变量,但在这个例子里,我们并不关心。我们的关注点是交互式shell提供的消息。它显示了三个一连串的转换。链中的每个函数都接收一个Int
,返回一个偏应用函数。不过,最后一个的结果是Unit
。
在curry时创建偏应用函数是Scala的内部事务。从实用的观点来看,它们让我们可以借助于语法糖传递函数值。这样,我们以curry化的形式重写上一节的inject()
方法:
FunctionValuesAndClosures/Inject4.scala
def inject(arr: Array[Int], initial: Int)(operation: (Int, Int) => Int) : Int = {
var carryOver = initial
arr.foreach(element => carryOver = operation(carryOver, element))
carryOver
}
上面这个版本的inject()
方法和早先的版本之间唯一的差别就在于多个参数列表。第一个参数列表接收两个参数,第二个接收一个,是个函数值。
如此一来,传递函数值就不必再在括号里用以逗号分隔的参数了。我们可以用更为优雅的大括号来调用这个方法:
FunctionValuesAndClosures/Inject4.scala
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)