Scala教程(十)函数与闭包详解
1 函数
1.1 本地函数
函数式编程风格的一个重要设计原则:程序应该被解构成若干小的函数,每个完成一个定义良好的任务。单个函数经常很小。这种风格的好处是它给了程序员许多可以灵活组装成更复杂事物的建造模块。每个小块应该充分简化到足以单独理解。
Scala中提供了可以把函数定义在另一个函数中。就好象本地变量那样,这种本地函数仅在包含它的代码块中可见。
def processData(fileName:String,width:Int){ // 本地函数 def processLine(line:String){ // 长度大数指定的长度,进行打印 if(line.length() > width){ println(fileName+":"+line) } } // 读取文件 val source = Source.fromFile("E:\\input.txt"); for(line <- source.getLines()){ processLine(line); } }
processLine的定义放在processFile的定义里。作为本地函数,processLine的范围局限于processFile之内,外部无法访问。
1.2 函数值
函数文本被编译进一个类,类在运行期实例化的时候是一个函数值:function value。因此函数文本和值的区别在于函数文本存在于源代码,而函数值存在于运行期对象。
// =>指明这个函数把左边的东西(任何整数x)转变成右边的东西(x + 1)。所以,这是一个把任何整数x映射为x + 1的函数。 var increase = (x:Int) => x+1; // 执行结果:11 println(increase(10)) // 因为increase是var,你可以在之后重新赋给它不同的函数值。 increase = (x: Int) => x + 9999 // 执行结果:10009 println(increase(10)); // 所有的集合类都能用到foreach方法。 val someNumbers = List(-11, -10, -5, 0, 5, 10) someNumbers.foreach((x: Int) => println(x))
输出结果:
-11
-10
-5
0
5
10
// filter 方法选择集合类型里可以通过用户提供的测试的元素。测试是通过函数的使用来提供的。例如,函数(x: Int) => x > 0可以被用作过滤。 someNumbers.filter((x: Int) => x > 0).foreach { (x:Int) => println(x) }
输出结果:
5
10
// 下划线当做一个或更多参数的占位符,只要每个参数在函数文本内仅出现一次 someNumbers.filter(_ > 0).foreach { (x:Int) => println(x) }
输出结果:
5
10
/* * _ + _将扩展成带两个参数的函数文本。 * 这也是仅当每个参数在函数文本中最多出现一次的情况下你才能使用这种短格式的原因。 * 多个下划线指代多个参数,而不是单个参数的重复使用。 * 第一个下划线代表第一个参数,第二个下划线代表第二个,第三个……,如此类推。 */ val f = (_: Int) + (_: Int) println(f(5, 10))
输出结果:15
1.3 偏应用函数
以这种方式使用下划线时,你就正在写一个偏应用函数:partially appliedfunction。
val data = List(1,2,3,4,5,6); // for简写遍历List data.foreach(println) data.foreach(println _) data.foreach(x=>println(x))
1.4 部分应用函数
比如定义了一个函数: def sum(a:Int,b:Int,c:Int) = a + b +c,当调用sum的时候,如果不提供所有的参数或某些参数还未知时,比如sum _ ,sum(1,_:Int,3), 这样就生成了所谓的部分应用函数。
部分应用函数只是逻辑上的一个表达,scala编译器会用Function1, Function2这些类来表示它。
// 定义了一个函数: def sum(a:Int,b:Int,c:Int) = a + b +c println(sum(1,2,3)); /* * 部分应用函数(Partial Applied Function)是缺少部分参数的函数,是一个逻辑上概念偏函数是只对函数定义域的一个子集进行定义的函数。 * 调用sum的时候,如果不提供所有的参数或某些参数还未知时,比如sum _ , sum(1,_:Int,3), 这样就生成了所谓的部分应用函数。 * 部分应用函数只是逻辑上的一个表达,scala编译器会用Function1, Function2这些类来表示它. */ val fp_a = sum _; println(( sum _).apply(1,2,3)); val fp_b = sum(1,_:Int,3) println(fp_b(2)) println(fp_b(10))
1.5 高阶函数实例
高阶函数:函数当作参数进行传递。
// 生成1到9集合,将其中的每个项都变成*,再次进行打印 (1.to(9)).map("*" * _).foreach(println);
输出结果:
*
**
***
****
*****
******
*******
********
*********
// 生成1到9集合,对2取产为0,进行打印输出 (1.to(9)).filter(_ % 2 == 0).foreach(println);
输出结果:
2
4
6
8
// 从右向左连乘 格式 (…((1 * 2) * 3) * … * 9) println((1.to(9)).reduceLeft(_ * _))
输出结果:362880
// 将内容进拆分 拆分后的单词进行拆 "Spark is the most exciting thing happening in big data today" .split(" ").sortWith(_.length < _.length) .foreach(println);<span lang="EN-US" style="font-family:Consolas;color:black;font-size:12.0pt;"> </span>
输出结果:
is
in
the
big
most
data
Spark
thing
today
exciting
happening
// 调用Java的方法:向上取整计算,它返回的是大于或等于函数参数,并且与之最接近的整数。 def ceil (x:Double) :Double = java.lang.Math.ceil(x); val fun = ceil _; val num =3.14; println(fun(num))输出结果: 4.0
// array数组进行循环调用函数 Array(3.14,1.42,2.0).map(fun).foreach(println);
输出结果: 4.0
2.0
2.0
// array数组元素 * 3 val triple = (x:Double) => 3 * x Array(3.14,1.42,2.0).map{ (x : Double) => 3 * x}.foreach(println); Array(3.14,1.42,2.0).map(triple).foreach(println);
输出结果:9.42
4.26
6.0
/* * (参数类型) => 结果类型 * 由于high_order_functions是一个接受函数参数的函数,因此它被称做高阶函数(higher-order function)。 */ def high_order_functions(f:(Double) => Double) =f(0.25); println(high_order_functions(ceil _))
输出结果:1.0
/* * 闭包写法 * mulBy函数有一个类型为Double的参数,返回一个类型为 (Double) => Double 的函数。因此,它的类型为:(Double) => ((Double) => Double) * mulBy的威力在于,它可以产出能够乘以任何数额的函数: */ def mulBy(factor:Double) = (x:Double) => factor* x val quintuple = mulBy(5) println(quintuple(20))输出结果: 100.0
// 参数(类型)推断 // Scala会尽可能帮助你推断出类型信息。举例来说,你不需要将代码写成: println(high_order_functions((x:Double) => 3 * x)) /* * 由于high_order_functions方法知道你会传入一个类型为 * (Double) => Double 的函数,你可以简单地写成: */ high_order_functions((x) => 3 * x) // 作为额外奖励,对于只有一个参数的函数,你可以略去参数外围的(): high_order_functions(x => 3 * x) // 如果参数在=>右侧只出现一次,你可以用_替换掉它: high_order_functions(3 * _)
输出结果:0.75
// 参数类型 val fun2 = 3 * (_:Double) val fun3:(Double) => Double = 3 *
2 闭包
2.1 定义闭包
add函数首次调用参数值为:1,该变量在(x:Int) => x +more函数的函数体内被引用,该值赋值a。然后参数变量more从运行时的栈上被弹出来。
接下来add函数再次被调用,这次参数值设置为了10,该变量在(x:Int) => x +more再次被引用,打印出计算结果的信息。
def add(more:Int) = (x:Int) => x +more val a = add(1); println(a(10)); val b = add(1000); println(b(10));
--以上为Scala的函数与闭包详解内容,谢谢大家对我的关注。
——厚积薄发(yuanxw)