Scala笔记整理(五):函数式编程
[TOC]
作为值传递的函数
测试代码如下:
package cn.xpleaf.bigdata.p4.function/** * scala中关于函数的操作 */object _01FunctionOps { def main(args: Array[String]): Unit = { functionOps1 } /** * 作为值传递的函数 * 将一个函数作为值传递给另外一个函数变量的时候,约定需要在函数后面加上:空格和下划线 * 相当于数据库中的别名,或者数据库表对应的视图 */ def functionOps1: Unit = { def sayHi(name:String) = println("Hello, " + name) def sayHello = sayHi _ sayHello("xpleaf") }}
输出结果如下:
Hello, xpleaf
匿名函数
测试代码如下:
package cn.xpleaf.bigdata.p4.function/** * scala中关于函数的操作 */object _01FunctionOps { def main(args: Array[String]): Unit = { functionOps2 } /** * 匿名函数,说白了就是没有函数名字 * 匿名函数就和java中的匿名内部类一样,是只适合使用一次的函数 * 一般如果一个函数的参数是一个函数,这种情况下多用匿名函数来作为参数进行传递 */ def functionOps2: Unit = { val printName = (name:String) => println("你好," + name) printName("xpleaf") }}
输出结果如下:
你好,xpleaf
其实前面在学习ArrayBuffer的时候已经有使用过匿名函数:
scala> val ab = ArrayBuffer[Int](3, 8, 2, 20, 5, 7)ab: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 8, 2, 20, 5, 7)scala> ab.sortWith((v1, v2) => v1 > v2)res209: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(20, 8, 7, 5, 3, 2)
带函数参数的函数(高阶函数)
说明的作用有两个(匿名函数作为参数和返回值为匿名函数),具体参考下面的测试代码:
package cn.xpleaf.bigdata.p4.function/** * scala中关于函数的操作 */object _01FunctionOps { def main(args: Array[String]): Unit = {// functionOps1// functionOps2 functionOps3 } /** * scala的高阶函数,就是函数的 [参数是函数] 的函数,把这种函数称为高阶函数 */ def functionOps3: Unit = { // 1.匿名函数作为参数 def highOrderFunc(name:String, func:(String) => Unit): Unit = { func(name) } highOrderFunc("xpleaf", (name:String) => println("Hello, " + name)) // 2.将匿名函数作为返回值传递给另外一个函数 def getGoodBayFunction(gMsg: String) = (gName: String) => println(gMsg + ", " + gName) val goodbayFunction = getGoodBayFunction("good bye") goodbayFunction("xpleaf") }}
输出结果如下:
Hello, xpleafgood bye, xpleaf
参数(类型)推断
测试代码如下:
package cn.xpleaf.bigdata.p4.functionimport scala.collection.mutable.ArrayBuffer/** * scala中关于函数的操作 */object _01FunctionOps { def main(args: Array[String]): Unit = {// functionOps1// functionOps2// functionOps3 functionOps4 } /** * 对于匿名函数的省略写法 */ def functionOps4: Unit = { val ab = ArrayBuffer[Int](1, 2, 3, 4, 5) // val newAB = ab.map((x:Int) => x * 100) // val newAB = ab.map((x) => x * 100) // val newAB = ab.map(x => x * 100) val newAB = ab.map(100 * _) println(newAB) // ArrayBuffer(100, 200, 300, 400, 500) }}
输出结果如下:
ArrayBuffer(100, 200, 300, 400, 500)
常见高阶函数--map函数
遍历集合中的每一个元素,返回也是一个集合,集合大小和之前集合相等。
- 快速产生0.1, 0.2, 0.3等方式的数字
scala> (1 to 9).map(0.1 * _).foreach(println(_))0.10.20.300000000000000040.40.50.60000000000000010.70000000000000010.80.9
- 打印三角形
scala> (1 to 9).map("*" * _).foreach(println(_))*********************************************
在这里,我们还用到了foreach,它和map很像,只不过它的函数并不返回任何值,foreach只是简单的将函数应用到每个元素而已。
常见高阶函数--filter函数
按照过滤条件,将原集合中不符合条件的数据过滤掉
- 输出所有匹配某个特定条件的元素,得到一个序列中的所有偶数
scala> (1 to 9).filter(line => line % 2 == 0).foreach(println(_))2468scala> (1 to 9).filter(_ % 2 ==0).foreach(println)2468
常见高阶函数--reduce函数
scala> (1 to 9).reduce((v1:Int, v2:Int) => v1 + v2)res4: Int = 45scala> (1 to 9).reduce(_ + _)res6: Int = 45scala> (1 to 9).reduceLeft(_ + _)res7: Int = 45scala> (1 to 9).reduceRight(_ + _)res8: Int = 45
可以写下面一个函数来验证reduce函数的执行过程:
scala> val process = (v1:Int, v2:Int) => println(s"v1=${v1}, v2=${v2}")process: (Int, Int) => Unit = scala> def pro(v1:Int, v2:Int):Int = { | println(s"v1=${v1}, v2=${v2}") | v1 + v2 | }pro: (v1: Int, v2: Int)Int
- reduce的执行流程
scala> (1 to 9).reduce((v1:Int, v2:Int) => pro(v1, v2))v1=1, v2=2v1=3, v2=3v1=6, v2=4v1=10, v2=5v1=15, v2=6v1=21, v2=7v1=28, v2=8v1=36, v2=9res0: Int = 45
- reductLeft的执行流程
scala> (1 to 9).reduceLeft((v1:Int, v2:Int) => pro(v1, v2))v1=1, v2=2v1=3, v2=3v1=6, v2=4v1=10, v2=5v1=15, v2=6v1=21, v2=7v1=28, v2=8v1=36, v2=9res2: Int = 45
- reductRight的执行流程
scala> (1 to 9).reduceRight((v1:Int, v2:Int) => pro(v1, v2))v1=8, v2=9v1=7, v2=17v1=6, v2=24v1=5, v2=30v1=4, v2=35v1=3, v2=39v1=2, v2=42v1=1, v2=44res3: Int = 45
这样的话执行过程就非常清晰了,reduce和reduceLeft都是从左边的操作数开始,而reduceRight是从右边的操作数开始。
常见高阶函数--sortWith函数
scala> (1 to 9).sortWith((v1:Int, v2:Int) => v1 > v2).foreach(println(_))987654321scala> (1 to 9).sortWith(_ > _).foreach(println)987654321
闭包
函数的变量不在其作用域内被调用,就是闭包的概念,看下面一个例子:
def closePackage: Unit ={ def mulBy(factor:Double) = (x:Double) => factor * x val triple = mulBy(3) val half = mulBy(0.5) println(triple(14) +" " + half(14)) //42, 7}
1)mulBy的首次调用将参数变量factor设为3,。该变量在(x:Double)=>factor *x
函数的函数体内被引用。该函数被存入triple.然后参数变量factor从运行时的栈上被弹出。
2)mulBy再次被调用,这次factor被设为0.5.该变量在(x:Double)=>factor *x
函数的函数体内被引用,该函数被存入half.
每一个函数被称为闭包(closure)。闭包有代码和代码用到的任何非局部变量定义构成。
其实上面的mulBy
函数就类似于下面这个:
def mulBy1(factor:Double, x:Double) = { factor * x}
原本还希望进一步写成下面这样子的:
def mulBy2(factor:Double, x:Double) = { def work(x:Double) = { factor * x } return work}
但在scala中不支持这样的写法,下面这样写才可以:
def mulBy2(factor:Double) = { (x:Double) => factor * x}// 加个return也不行
之前在Python中使用过闭包,类似上面的例子,Python就可以这样写:
def mulBy(factor): def work(x): return factor * x return work
测试如下:
>>> def mulBy(factor):... def work(x):... return factor * x... return work...>>> triple = mulBy(3)>>> half = mulBy(0.5)>>> triple(14)42>>> half(14)7.0
当然,只是Python的语法格式就没有scala那么灵活了。
柯里化(currying函数)
1、柯里化(currying)指的是将原来接受2个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数作为参数的函数。
2、在函数调用的过程中,就变为了两个函数连续调用的形式。在Spark源码中,也有体现,所以对()()这种形式的Curring函数,一定要掌握。
以下函数接受一个参数,生成另一个接受单个参数的函数,要计算两个数的乘积,调用如下:
/** * curryingFunction: 柯里化函数 */def curryingFunction ={ def totalSum(x:Int, y:Int) = println( x + y) //totalSum(4,5) def totalSumAtTime(x:Int) = (y:Int) => x + y println(totalSumAtTime(4)(5))}