本篇文章重点说明什么是函数柯里化,这个语法现象的背后动机是什么,有什么样的应用场景,以及与部分应用函数(Partial Applied Function)之间的联系
1. 什么是柯里化函数
A way to write functions with multiple parameter lists. For instance def f(x: Int)(y: Int) is a curried function with two parameter lists. A curried function is applied by passing several arguments lists, as in: f(3)(4). However, it is also possible to write a partial application of a curried function, such as f(3).
也就是说,有多个参数列表 的函数就是柯里化函数,所谓的参数列表就是使用小括号括起来的函数参数列表
2. 柯里化函数的设计动机
每个语言现象都有它的动机,即为解决什么问题而引入了柯里化函数这个语言现象
以如下的柯里化函数sum为例,Scala在执行sum(1)(2)的时候,实际上是执行了两次函数调用,首先执行sum(1)_返回一个函数,这个函数是一个部分应用函数,为这个部分应用函数提供参数2后,得到结果
scala> def sum(x:Int)(y:Int)=x+y sum: (x: Int)(y: Int)Int scala> val second = sum(1)_ second: Int => Int = <function1> scala> second(2) res1: Int = 3 scala>
sum(1)_这个偏函数表示的是第二个函数
柯里化函数执行时,分解为两个函数执行,步骤与下面的方法调用过程类似
scala> def first(x:Int) = (y:Int)=>x+y first: (x: Int)Int => Int scala> val second = first(1) second: Int => Int = <function1> scala> second(2) res4: Int = 3
柯里化实现Java7的try-with-resources控制结构
package spark.examples.scala import java.io.FileInputStream object CurryTest { def withIOStream(stream: java.io.InputStream)(func: java.io.InputStream => Unit) { try { func(stream) } finally { stream.close() } } def read(stream: java.io.InputStream) = { println(stream.available()) } def main(args: Array[String]) { withIOStream(new FileInputStream("d:/people.txt")) { println("Hello,Start to read") read } }
柯里化函数withIOStream中的func参数也可以和stream放在一起,使得withIOStream成为一个普通的函数,但是使用者在用的时候,就无法实现像CurryTest中使用的那样体现Java7的try-with-resource风格,具有明显的代码控制结构的意味在里面。
curry化最大的意义在于把多个参数的function等价转化成多个单参数function的级联,这样所有的函数就都统一了,方便做lambda演算。 在scala里,curry化对类型推演也有帮助,scala的类型推演是局部的,在同一个参数列表中后面的参数不能借助前面的参数类型进行推演,curry化以后,放在两个参数列表里,后面一个参数列表里的参数可以借助前面一个参数列表里的参数类型进行推演。这就是为什么 foldLeft这种函数的定义都是curry的形式
object FoldLeftRightTest { def main(args: Array[String]) { val list = List(1, 3, 5) //折叠操作是一个递归的过程,将上一次的计算结果代入到函数中 //作为结果的参数在foldLeft是第一个参数,在foldRight是第二个参数 //foldLeft表示从左向右折叠,从最左边的元素向最右边的元素折叠 val c = list.foldLeft("String:")((x: String, y: Int) => x + y) //String:135 println(c) val cc = list.foldLeft("String:")((x: String, y: Int) => y + x) //531String: println(cc) /* /:是foldLeft的符号表示*/ val ccc = list./:("String:")((x: String, y: Int) => x + y) //String:135 println(ccc) val d = list.foldLeft(100)((x: Int, y: Int) => x + y) //109 println(d) //foldRight表示从右向左折叠,从最右边的元素向最左边的元素折叠 val e = list.foldRight("String:")((y: Int, x: String) => x + y) //String:531 println(e) val f = list.foldRight("String:")((y: Int, x: String) => y + x) //135String: println(f) //:\是foldRight的符号表示 val g = list.:\("String:")((y: Int, x: String) => y + x) //135String: println(g) } }
关于柯里化函数类型推演的例子:
class A { class B } object CurryTest { //定义为柯里化函数时没有问题的,b的类型可以由a参与得出 def method(a: A)(b: a.B) { } //参数列表的第二个参数,a没有定义,不能参与b的类型推断 def method2(a: A, b: a.B /*a is not defined in a.B*/) { } def main(args: Array[String]) { } }