首页 > 代码库 > 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")