首页 > 代码库 > Programming In Scala Reading Note 7

Programming In Scala Reading Note 7

函数和闭包

1 成员方法

java中函数存在的方式,我们给一个类追加一个功能的途径就是给他追加一个方法。

 

2 本地方法

所谓的本地方法就是存在于一个方法内部的方法。

如果一个类中有一个方法是private的,且只有一个方法使用到他,那么这个方法完全可以被定义为一个本地方法

 

3 函数式一等公民

方法分为:

1 方法名称  def add

2 方法参数      (a:Int, b:Int)

3 赋值符号              =

4 方法体                  a + b

其实,(a:Int, b:Int) => a + b,就构成了一个基本的函数了,只不过它的名字叫做add,所以说我们可以这么调用一个函数:

add(1, 2),返回值为3

也可以这么调用这个函数:

((a:Int, b:Int) => a + b)(1, 2),返回值也是3

 

现在就应该相对明朗了,我们可以把一个函数赋给一个val变量,这样这个变量就是一个函数了,如:

val addition = (a:Int, b:Int) => a + b

那么addtion(11, 12)就等于23。

 

另外方法可以被当成函数的参数来使用,如List的foreach方法,它需要一个方法作为参数

val list = List(1, 2, 3, 4, 5)

list.foreach(element => println(element + 1))

另外一个filter方法,

val evenList = list.filter(element => element % 2 == 0)

 

4 更加简洁

像上面的红字部分提供了简写方法,而实际上完整的格式应该是list.foreach((element:Int) => println(element + 1))

我们没有理由去写这么完整的东西,除非编译器无法确定这个方法参数的类型了。

 

还有更加简洁的方式,占位符写法:

用这个例子:val evenList = list.filter(element => element % 2 == 0)

这个例子中只有一个参数,且在方法体中出现一次,那么可以简写为占位符:

val evenList = list.filter(_ % 2 == 0),就是说,对每一个元素都用_替代。

 

还可以这样子呢: val threeParamAdd = (_:Int) + (_:Int) + (_:Int)

这也是每一个参数在方法体中出现一次,第一个参数替代第一个_,第二个替代第二个_,以此类推。。。

 

我们尽量去省略参数类型,知道编译器不能为我们判别出来我们的参数类型。

 

Partially applied functions(部分(不完全)应用(提供)函数):

list.foreach(x => println(x)) 改写为 list.foreach(println _)

_不仅可以代表单独的一个参数,还可以代表全部的参数。

当我们没有给一个方法提供足够的参数,而只是在方法名后面紧跟一个空格加上一个下划线,如println _

 

def println(x: Any) = Console.println(x)

是Predef类中的方法,我们只提供一个println _,方法这个_代表的是这个方法中的所有的参数。

 

def sum(a: Int, b: Int, c: Int) = a + b + c

如果我们把他给一个变量时,可以这么写

val threeParamAdd = sum _                部分应用函数

或者

val threeParamAdd = (_:Int) + (_:Int) + (_:Int)      占位符写法

或者

val threeParamAdd = (a:Int, b:Int, c:Int) => a + b + c   普通写法

 

回过来说val threeParamAdd = sum _, 当对这个threeParamAdd进行调用的时候,如threeParamAdd(1, 2, 3),编译器会看到sum _,然后他知道去找sum函数,发现有三个参数,依次替代,完成计算。

还会有另外的情况,如:

val oneAddSomeAddThree = sum(1, _:Int, 3),这样的话,我们的oneAddSomeAddThree只允许有一个参数了。

 

如果某个地方整好需要一个函数作为参数,且这个函数可以使用部分应用函数的话,这个时候下划线也可以省略。

如:list.foreach(println _) 可以修改成 list.foreach(println)

因为编译器知道我们写的这个println是一个函数,而不是别的东西,他会把他认为是一个部分应用函数。

 

5 闭包

引用了自由变量的函数,与这个自由变量一起组成的实体,叫做一个闭包

自由变量,除了函数的参数,以及函数中声明的变量以外的变量。

绑定变量,函数的参数,以及其他函数编译时能够确定的变量。

 

封闭术语:closed term,不包含自由变量的函数文本。

开放术语:open term,包含了自由变量的函数文本。

 

6 特殊的调用函数的形式

1 重复的参数:

def echo(arr: String*) { arr.foreach(println _) }

arr:String*, 是scala的语法,跟java中的String ... some,相似。

其中*指明这个参数是一个数组,数组元素的类型是String,数组长度为调用方法时给定的字符串的个数。

 

如果现在有一个数组,想用以上方法调用,如:

val some = Array("This‘s", "little", "wonderful"),如果直接调用的话,会出错,echo(some)

这时需要这样传递参数:echo(some: _*)

编译器会把some中的每一个元素作为参数传递给方法。

 

2 命名的参数:

def speed(distance:Float, time:Int) = distance / time

speed(100, 10)

speed(distance = 100, time = 10)

speed(time = 10, distance = 100)

 

3 具有默认值的参数

def nameBreaker(name:String = "Voctrals Lou", breaker:String = " ") = name.split(breaker)

如果我们调用nameBreaker(),返回Array("Voctrals", "Lout")

如果我们提供了其中的值,那么默认值就不再起作用了,如

nameBreaker(breaker = "_"),返回值为Array("Voctrals Lou")