第3讲:Scala函数式编程彻底精通

来源:互联网 发布:idea运行单个java文件 编辑:程序博客网 时间:2024/05/16 06:20

本篇博文主要是对Scala函数式编程的彻底详解,涉及高阶函数,闭包,颗粒化等详解。
1. Scala函数式编程
Scala中的函数的可以不依赖类或则借口,独立存在,甚至函数可以作为参数传递,可以直接赋值给变量。
Scala的函数式编程使得算法的设计可以更高效更精简,因为函数式是依赖人们的思考 方式构建的。
Scala的函数式编程是的开发代码行数更少。
Spark中的计算几乎所有都是用函数式编写的,而且我们在处理数据一般都是针对集合的,集合的函数式编程更是重中之重,以及基于scala的函数式操作集合。
高阶函数:
如果一个函数是一个函数的参数则称为此函数为高阶函数
高阶函数是scala与Java的最大不同。

scala> def fun1(name : String){println(name)}//fun1为函数的名称,(name:String)参数 Unit是返回值为Unit//为啥返回的值是Unit,因为println不会直接参数结果,他只会具体完成工作。    fun1: (name: String)Unit//现在将函数赋值给变量//val fun1_v = fun1 _   //函数名 _   中间一定要加空格//这时候的fun1_v就是函数了scala> val fun1_v = fun1 _   //fun1 _ 此时就表示函数本身了//此时我们看出fun1_v就是函数了,参数的类型是String类型,返回值是Unit//  =>   是将左边的参数进行右边的加工。    fun1_v: String => Unit = <function1>scala> fun1("Spark")    Sparkscala> fun1_v("Spark")    Sparkscala> fun1_v("Scala")    Scala

匿名函数
在实际工作的时候,比如算法设计我们可能不需要函数名称,只需要函数执行的功能就可以了,这时候我们就会使用匿名函数。
但是我们要使用它,就可以借助函数赋值给变量,变量就变成了函数的性质,将匿名函数赋值给变量。
匿名函数的定义规则:
(参数 :类型) => 函数的操作

scala> val fun2 = (content : String) => println(content)    fun2: String => Unit = <function1>scala> fun2("Hadoop")    Hadoop

高阶函数
函数的参数也是函数,为啥可以?因为前面谈到了函数可以赋值给变量,而我们现在直接把函数作为函数的参数,也应该是可以的。
这样的设计非常强大:
例如:我们使用函数去操作集合,可能需要循环遍历集合,这个时候我们就可以使用函数参数,而此时的函数参数具有遍历集合的功能。

//第一个参数,定义了一个函数,func是函数的名称,(String)是变量的类型,=>Unit指定函数的返回值是Unit//第一个传入参数的要求是: 定义一个函数,函数值是Unitscala> val hiScala = (content : String) => println(content)    hiScala: String => Unit = <function1>scala> def bigData(func : (String) => Unit,content:String){func(content)}    bigData: (func: String => Unit, content: String)Unit//传入的第一个参数是一个函数,传入第二个参数的时候,content就会作为参数传入第一个hiScala函数里面scala> bigData(hiScala,"Spark")    Spark//item => (2*item) 是一个匿名函数,作为参数传入到map()函数中,map函数的作用 是循环遍历集合中的所有元素。scala> val array = Array(1,2,3,4,5,6,7)    array: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7)scala> array.map(item => (2*item))    res5: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14)

另外,高阶函数的返回值也有可能是函数

scala> def func_Returned(content : String) = (message : String) => println(message)// 匿名函数本身是返回值,所有返回类型是Unit,func_returned()函数的返回值类型是String.    func_Returned: (content: String)String => Unit scala> func_Returned("Spark")//执行结果是一个函数//输入类型是字符串String,为啥呢?因为 (message : String) 输入参数是String,返回类型是Unit,因为println(message)是一条打印语句。    res7: String => Unit = <function1>scala> def func_Returned(content : String) = (message : String) => println(content + " " +  message)    func_Returned: (content: String)String => Unitscala> val returned = func_Returned("Spark")    returned: String => Unit = <function1>//为啥会打印 Spark 此时的Spark是上面def func_Returned(content : String) = (message : String) => println(content + " " + message)为参数的输入值//为啥会打印Scala 因为returned = func_Returned("Spark"),是把函数的返回值为函数的返回值赋值给了returned,也就相当于把(message : String) => println(content + " " + message) 赋值给了returned,此时我们传入参数,也就是message的参数,content之前传过了。scala> returned("Scala")    Spark  Scala

高阶函数的两个层面:
1. 函数的参数是函数
2. 函数的返回值是函数
高阶函数有一个重要的性质就是类型推断,可以自动推断出具体的参数和类型,并且对于只有一个参数的函数,可以省略掉小括号,如果在参数作用的函数体内,只使用一次输入参数的参数值的话,那么可以将函数的输入参数的名称省略,用下划线 _ 来代替。

//之前定义函数的时候是func : (content : String),因为函数中就一个参数,就可以将函数的参数名省略。scala> def spark(func:(String) => Unit,name:String){func(name)}    spark: (func: String => Unit, name: String)Unitscala> spark((name : String) => println(name),"Scala")    Scalascala> spark((name) => println(name),"Scala")    Scala//为啥可以省略掉String,因为我们定义的时候传入的参数是String类型,而且确实传入的值也是String类型,scala可以进行类型推导,所以可以省略。scala> spark(name => println(name),"Scala")//如果只有一个参数的时候 () 也可以省略掉了。    Scalascala> spark(println(_),"Scala")//因为函数体本身只有一个参数,所以可以将参数省略掉,用下划线代替。    Scalascala> spark(println , "Scala")//如果只有一个参数的时候,() 也可以省略。    Scalascala> val array = Array(1,2,3,4,5,6,7,8,9)    array: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)scala> array.map(item => (2*item))    res5: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18)//因为只有一个参数所以可以省略参数名,直接用下划线代替。scala> array.map(2*_)    res13: Array[Int] = Array(2, 4, 6, 8, 10, 12, 14, 16, 18)//此时filter是高阶函数scala> array.map(2*_).filter(_>10).foreach(println)12141618//(_+_)里面就是一个函数,第一个_的值是前n次求和的结果,第二个_ 是第n+1项的结果。scala> (1 to 100).reduceLeft(_+_)    res19: Int = 5050

闭包:
函数的变量,超出他的有效作用域中我们还能对函数的内部变量进行访问。

scala> def scala(content : String) = (message : String) => println(content + " : " + message)    scala: (content: String)String => Unitscala> val funcResult = scala("Spark")//通用的角度来看,scala函数执行之后,spark是不会存在的。因为content是scala的局部变量。在函数执行完之后是不会存在的。    funcResult: String => Unit = <function1>scala> funcResult("Flink")    Spark : Flink//这里为啥还可以打印出来Spark//而scala执行完之后,里面的成员依旧可以被访问这就是闭包。//也就是说content的内容被保存在函数体内部可以被反复的使用。//闭包的实现原理是:Scala为我们当前的函数生成了一个当前我们看不到的对象,把我们对象的content成员,而scala函数也是对象的成员,当我们执行scala函数的时候,也就是执行对象里面的函数,而对象里面的函数,访问函数里面的属性成员是非常正常的。

颗粒化:
作用是将两个参数的函数,转换成两个函数,第一个函数的参数为两个参数函数的第一个参数,同理,第二个函数的参数为第二个参数。

scala> def sum(x:Int,y:Int) = x + y    sum: (x: Int, y: Int)Intscala> sum(1,2)    res16: Int = 3scala> def sum_Currying(x:Int) = (y:Int) => x + y    sum_Currying: (x: Int)Int => Intscala> sum_Currying(1)(2)    res17: Int = 3scala> def sum_Currying_Better(x : Int)(y : Int) = x + y    sum_Currying_Better: (x: Int)(y: Int)Intscala> sum_Currying_Better(1)(2)    res18: Int = 3

集合:

//创建一个集合scala> val list = List("Scala","Spark","Fink")    list: List[String] = List(Scala, Spark, Fink)//map函数会遍历整个集合,"The content is : " + _ 是一个函数,因为每个参数只用一次所以我们用下划线 _ 代替。scala> list.map("The content is : " + _)    res20: List[String] = List(The content is : Scala, The content is : Spark, The content is : Fink)scala> val cal = list.map("The content is : " + _)    cal: List[String] = List(The content is : Scala, The content is : Spark, The content is : Fink)scala> cal    res21: List[String] = List(The content is : Scala, The content is : Spark, The content is : Fink)scala> cal.flatMap(_.split(" "))//拆分    res22: List[String] = List(The, content, is, :, Scala, The, content, is, :, Spark, The, content, is, :, Fink)scala> cal.flatMap(_.split(" ")).foreach(print)    Thecontentis:ScalaThecontentis:SparkThecontentis:Finkscala> list.zip(List(10,6,5))//赋值,集合    res24: List[(String, Int)] = List((Scala,10), (Spark,6), (Fink,5))

作业:统计一个文件夹下面的所有的单词出现的总次数
1. 文件个数
2. 文件里面的文件怎么统计

package ThirdWordCountobject WordCounter {  //导入jar包  import scala.io.Source  import java.io._  //存储单词和个数  var map = Map.empty[String, Int]  def main(args: Array[String]): Unit = {    scanDir(new File("E://aa"))    map.foreach(f =>      println(f)      )  }  def scanDir(dir: File): Unit = {    dir.listFiles.foreach { file =>       if(file.isFile()){        readFile(file)        println(file)      }          }  }  def readFile(file: File){    val f = Source.fromFile(file)    for (line <- f.getLines()){      count(line)    }  }
0 0