Appearance
Scala中的函数可以独立定义和存在,它们可以直接作为值被赋值给变量。根据Scala的语法规定,当将函数赋值给变量时,必需在函数名后面加上空格和下划线。原因是这样可以提高代码的可读性,并避免与变量名冲突。
scala
def sayHello(name: String) {
println("Hello, " + name)
}
val sayHelloFunc = sayHello _
sayHelloFunc("张三")
}
匿名函数
匿名函数是一种没有名称的函数,可以通过以下语法格式进行定义:(参数名:参数类型) => 函数体
其中,参数名和参数类型是函数的参数列表,函数体是函数的具体实现。可以将匿名函数直接赋值给某个变量。
scala
val sayHelloFunc = (name: String) => println("Hello, " + name)
// 如果是多行,需要添加{}
val sayHelloFunc = (name: String) => {
name += "0"
println("Hello, " + name)
}
高阶函数
接收其它函数作为当前函数的参数,当前这个函数也被称作高阶函数。
scala
// 1. 先定义一个匿名函数,赋值给变量sayHelloFunc
val sayHelloFunc = (name: String) => println("Hello " + name)
// 2. 再定义一个高阶函数,这个高阶函数的参数会接收一个参数
def greeting(func: (String) => Unit, name: String) {
func(name)
}
// 3. 将值传递进去
greeting(sayHelloFunc, "张三")
// 4. 也可以使用匿名函数的写法
greeting((n: String) => println(n), "张三") // 简写:greeting(n => println(n), "张三")
整理使用方法如下所示:
scala
// 先定义一个高阶函数
def greeting(func: (String) => Unit, name: String) {
func(name)
}
// 使用高阶函数:完整写法
greeting((name: String) => println("Hello, " + name), "scala")
// 使用高阶函数:高阶函数可以自动推断出参数类型,而不需要写明类型
greeting((name) => println("Hello, " + name), "scala")
// 使用高阶函数:对于只有一个参数的函数,还可以省去其小括号
greeting(name => println("Hello, " + name), "scala")
常用高阶函数
刚才是自行实现的高阶函数,但在工作中使用自己定义高阶函数的场景不多,大部分场景都是去使用已有的高阶函数。
- map:对传入的每个元素都进行处理,返回一个元素
- flatMap:对传入的每个元素都进行处理,返回一个或者多个元素
- foreach:对传入的每个元素都进行处理,但是没有返回值
- filter:对传入的每个元素都进行条件判断,如果返回true,则保留该元素,否则过滤掉该元素
- reduceLeft:从左侧元素开始,进行reduce操作
map
使用方式如下,如果只有一行可以去除{}
,也可以简写成_
scala
Array(1, 2, 3, 4, 5).map(n => {n * 2}) // 2, 4, 6, 8, 10
Array(1, 2, 3, 4, 5).map(n => n * 2)
Array(1, 2, 3, 4, 5).map(_ * 2)
flatMap
scala
Array("hello you", "hello me").flatMap(line => line.split(" ")) // hello, you, hello, me
Array("hello you", "hello me").flatMap(_.split(" "))
foreach
scala
Array(1, 2, 3, 4, 5).map(_ * 2).foreach(num=>println(num))
Array(1, 2, 3, 4, 5).map(_ * 2).foreach(println(_))
Array(1, 2, 3, 4, 5).map(_ * 2).foreach(println _)
Array(1, 2, 3, 4, 5).map(_ * 2).foreach(println)
filter
scala
Array(1, 2, 3, 4, 5).filter(num=>num % 2 == 0)
Array(1, 2, 3, 4, 5).filter(_ % 2 == 0)
reduceLeft
表示先对元素1和元素2进行处理,然后将结果与元素3处理,再将结果与元素4处理,依次类推;spark中有一个reduce函数,和这个函数的效果一致
scala
Array(1, 2, 3, 4, 5).reduceLeft((t1, t2) => t1+t2)
Array(1, 2, 3, 4, 5).reduceLeft( _ + _)
案例:函数式编程
统计多个文本内的单词总数,使用scala的IO包读取文本文件内的数据。
scala
val lines01 = scala.io.Source.fromFile("D://a.txt").mkString
val lines02 = scala.io.Source.fromFile("D://b.txt").mkString
注意:下面这一行是核心代码,使用了链式调用的函数式编程。
scala
lines.flatMap(_.split( " ")).map((_, 1)).map(_._2).reduceLeft(_ + _)
lines.flatMap(_.split( " "))
:表示对每一行数据使用空格进行切割,返回每一个单词.map((_, 1))
:针对每一个单词,都转成tuple类型,tuple中的第1个元素是这个单词,第2个元素表示单词出现的次数.map(_._2)
:迭代每一个tuple,获取tuple中的第2个元素.reduceLeft(_ + _)
:对前面获取到的元素进行累加求和