柯里化(currying, 以逻辑学家Haskell Brooks Curry的名字命名)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数做为参数的函数。 在Scala中方法和函数有细微的差异,一般编译器会自动完成方法到函数的转换。若是想了解Scala方法和函数的具体区别,请参考博文Scala基础 - 函数和方法的区别。java
Scala中柯里化方法的定义形式和普通方法相似,区别在于柯里化方法拥有多组参数列表,每组参数用圆括号括起来,例如:app
def multiply(x: Int)(y: Int): Int = x * y
multiply方法拥有两组参数,分别是(x: Int)和(y: Int)。
multiply方法对应的柯里化函数类型是:ide
Int => Int => Int
柯里化函数的类型声明是右结合的,即上面的类型等价于:函数
Int => (Int => Int)
代表该函数若只接受一个Int参数,则返回一个Int => Int类型的函数,这也和柯里化的过程相吻合。.net
咱们仍以上面定义的multiply方法为例探索柯里化的一些细节:code
def multiply(x: Int)(y: Int): Int = x * y
上面的代码定义了一个柯里化方法,在Scala中能够直接操纵函数,可是不能直接操纵方法,因此在使用柯里化方法前,须要将其转换成柯里化函数。最简单的方式是使用编译器提供的语法糖:blog
val f = multiply _
返回的函数类型是:ip
Int => Int => Int
使用Scala中的部分应用函数(partially applied function)技巧也能够实现转换,可是请注意转后后获得的并非柯里化函数,而是一个接受两个(而不是两组)参数的普通函数:资源
val f = multiply(_: Int)(_: Int)转后后获得的类型为:get
(Int, Int) => Int其实就是一个接受两个Int型参数的普通函数类型。
先传入第1个参数:
val f1 = f(1)
返回类型为:
Int => Int
即一个接受一个Int参数返回Int类型的函数。 继续传入第2个参数:
val f2 = f1(2)
返回类型为:
Int
两组参数都已经传入,返回一个Int类型结果。
下面代码定义一个普通方法multiply1
和一个currying方法multiply2
,并将其转换相应的函数类型:
def multiply1(x: Int, y:Int, z:Int) = x * y * z val partialAppliedMultiply = multiply1 _ //类型:(x: Int, y: Int, z: Int) => Int def multiply2(x: Int)(y: Int)(z: Int) = x * y * z val curryingMultiply = multiply2 _ //类型:Int => (Int => (Int => Int))
在调用时,curryingMultiply能够依次传入各个参数,而partialAppliedMultiply在传入部分参数时,必须显示指定剩余参数的占位符:
val curryingMultiply1 = curryingMultiply(1) //类型:Int => (Int => Int) val partialAppliedMultiply1 = partialAppliedMultiply(1, _:Int, _: Int) //类型:(Int, Int) => Int
另外,curryingMultiply1的类型仍然是currying类型,而partialAppliedMultiply1的类型仍然是普通函数类型。
对于一些通用的操做能够实现成控制抽象,例如像文件打开、关闭操做。实现成控制抽象的好处是,能够在使用的时候,看起来更像是语言级别提供的功能。
无参函数的类型是() => T
,在使用时为了简化能够省略(),例如:
def runInThread(block: => Unit){ new Thread { override def run() { block } }.start() }
这样定义以后,在使用的时候就能够省略() =>,
runInThread{ println("Hi") }
若是方法只有一个参数,则可使用{}替代(),例如:
runInThread{ println("Hi") }
与传名参数相对的是传值参数。传值参数在函数调用以前表达式会被求值,例如Int,Long等数值参数类型;传名参数在函数调用前表达式不会被求值,而是会被包裹成一个匿名函数做为函数参数传递下去,例如高阶函数的函数参数就是传名参数。
withPrintWriter是一个柯里化方法,它接受两组参数,第1组参数是待操做的文件资源,第2组参数是操做文件资源的函数:
def withPrintWriter(file: File)(op: PrintWriter => Unit) { val writer = new PrintWriter(file) try { op(writer) } finally { writer.close() } }
用法以下:
withPrintWriter(new File("date.txt")) { writer => writer.println(new java.util.Date) }
withPrintWriter确保文件资源在被使用以后必定会被关闭,而且在使用的时候,看起来更像是语言内置的关键字函数。