什么是函数式编程

Scala是一门既面向对象,又面向过程的语言。
因此在Scala中有非常好的面向对象的特性,可以使用Scala来基于面向对象的思想开发大型复杂的系统和工程;
而且Scala也面向过程,因此Scala中有函数的概念。
在Scala中,函数与类、对象一样,都是一等公民,所以说scala的面向过程其实就重在针对函数的编程了,所以称之为函数式编程

函数赋值给变量

Scala中的函数是一等公民,可以独立定义,独立存在,而且可以直接将函数作为值赋值给变量

Scala的语法规定,将函数赋值给变量时,必须在函数后面加上空格和下划线

来看一个案例:将函数赋值给变量

  1. object Demo1 {
  2. //定义一个函数
  3. def sayHello(name: String): Unit = {
  4. println("hello," + name)
  5. }
  6. //将函数赋值给变量
  7. val sayHelloFunc = sayHello _
  8. def main(args: Array[String]): Unit = {
  9. sayHello("scala")
  10. sayHelloFunc("world")
  11. }
  12. }

image.png

匿名函数【val a=(…)=>{…}】

Scala中的函数也可以不需要命名,这种函数称为匿名函数
匿名函数的语法格式:(参数名: 参数类型) => 函数体
可以将匿名函数直接赋值给某个变量

object Demo2 {

  val sayHelloFunc = (name: String) => {
    println("hello," + name)
  }

  def main(args: Array[String]): Unit = {
    sayHelloFunc("world")
  }

}

image.png

高阶函数(参数里有函数)

由于函数是一等公民,所以说我们可以直接将某个函数作为参数传入其它函数
接收其它函数作为当前函数的参数,当前这个函数也被称作高阶函数 (higher-order function)
高阶函数可以自动推断出它里面函数的参数类型,对于只有一个参数的函数,还可以省去小括号

object Demo3 {

  //定义一个匿名函数,赋值给变量sayHelloFunc
  val sayHelloFunc = (name: String) => println("hello," + name)

  /**
   * 定义一个高阶函数,这个高阶函数的参数会接收一个函数
   *
   * @param func (String) => Unit 表示这个函数接收一个字符串,没有返回值
   * @param name name
   */
  def greeting(func: (String) => Unit, name: String): Unit = {
    func(name)
  }

  def main(args: Array[String]): Unit = {

    greeting(sayHelloFunc, "scala")

    //直接把匿名函数的定义传过来也是可以
    greeting((name: String) => println("hello," + name), "scala")

    //高阶函数可以自动推断出参数类型,而不需要写明类型
    greeting((name) => println("hello," + name), "scala")

    //对于只有一个参数的函数,还可以省去其小括号
    greeting(name => println("hello," + name), "scala")

  }

}

image.png

常用高阶函数

刚才是我们自己实现的高阶函数,其实我们在工作中自己定义高阶函数的场景不多,大部分场景都是去使用已有的高阶函数
下面我们来看几个常见的高阶函数

  • map:对传入的每个元素都进行处理,返回一个元素
  • flatMap:对传入的每个元素都进行处理,返回一个或者多个元素
  • foreach:对传入的每个元素都进行处理,但是没有返回值
  • filter:对传入的每个元素都进行条件判断,如果返回true,则保留该元素,否则过滤掉该元素
  • reduceLeft:从左侧元素开始,进行reduce操作

    map

    对传入的每个元素都进行处理,返回一个元素
    scala> Array(1, 2, 3, 4, 5).map(num => num * 2)
    res0: Array[Int] = Array(2, 4, 6, 8, 10)
    
    可以简写为:
    scala> Array(1, 2, 3, 4, 5).map(_ * 2)
    res5: Array[Int] = Array(2, 4, 6, 8, 10)
    

    flatMap

    对传入的每个元素都进行处理,返回一个或者多个元素
    scala> Array("hello you", "hello me").flatMap(item => item.split(" "))
    res1: Array[String] = Array(hello, you, hello, me)
    
    可以简写为:
    scala> Array("hello you", "hello me").flatMap(_.split(" "))
    res2: Array[String] = Array(hello, you, hello, me)
    

    foreach

    对传入的每个元素都进行处理,但是没有返回值
    scala> Array(1, 2, 3, 4, 5).map(num => num * 2).foreach(num => println(num))
    2
    4
    6
    8
    10
    
    可以简写为:
    scala> Array(1,2,3,4,5).map(_*2).foreach(println(_))
    2
    4
    6
    8
    10
    

    filter

    对传入的每个元素都进行条件判断,如果返回true,则保留该元素,否则过滤掉该元素
    scala> Array(1, 2, 3, 4, 5).filter(num => num % 2 == 0)
    res6: Array[Int] = Array(2, 4)
    
    可以简写为:
    scala> Array(1, 2, 3, 4, 5).filter(_ % 2 == 0)
    res7: Array[Int] = Array(2, 4)
    

    reduceLeft

    从左侧元素开始,进行reduce操作
    scala> Array(1, 2, 3, 4, 5).reduceLeft((t1, t2) => t1 + t2)
    res8: Int = 15
    
    可以简写为:
    scala> Array(1, 2, 3, 4, 5).reduceLeft(_ + _)
    res8: Int = 15
    
    scala> Array(1, 2, 3, 4, 5).sum
    res9: Int = 15
    

    综合案例

    统计文件内单纯总数

a.txt

hello you hello me

b.txt

hello hehe hello haha

使用scala的io包读取文本文件内的数据

import scala.io.Source.fromFile

val lines01 = fromFile("D://a.txt").mkString

val lines02 = fromFile("D://b.txt").mkString

使用List的伴生对象,将多个文件内的内容创建为一个List

val lines=List(lines01,lines02)

注意:下面这一行是核心代码,使用了链式调用的函数式编程

lines.flatMap(_.split( " ")).map((_, 1)).map(_._2).reduceLeft(_ + _)

lines.flatMap(_.split( " ")):表示对每一行数据使用空格进行切割,返回每一个单词
.map((_, 1)):针对每一个单词,都转成tuple类型,tuple中的第1个元素是这个单词,第2个元素表示单词出现的次数1
.map(_._2):迭代每一个tuple,获取tuple中的第2个元素,取Tuple元素需要带下划线
.reduceLeft(_ + _):对前面获取到的元素进行累加求和,可以使用sum代替

拆分演示:

scala> lines.flatMap(item=>item.split(" "))
res11: List[String] = List(hello, you, hello, me, hello, hehe, hello, haha)

scala> lines.flatMap(_.split(" ")).map(word=>(word,1))
res12: List[(String, Int)] = List((hello,1), (you,1), (hello,1), (me,1), (hello,1), (hehe,1), (hello,1), (haha,1))

scala> lines.flatMap(_.split(" ")).map((_, 1))
res13: List[(String, Int)] = List((hello,1), (you,1), (hello,1), (me,1), (hello,1), (hehe,1), (hello,1), (haha,1))

scala> lines.flatMap(_.split(" ")).map((_, 1)).map(tup => tup._2)
res14: List[Int] = List(1, 1, 1, 1, 1, 1, 1, 1)

scala> lines.flatMap(_.split(" ")).map((_, 1)).map(_._2)
res15: List[Int] = List(1, 1, 1, 1, 1, 1, 1, 1)

scala> lines.flatMap(_.split(" ")).map((_, 1)).map(_._2).sum
res16: Int = 8

核心代码也可以是:

lines.flatMap(item=>item.split(" ")).length