Scala中的函数式编程
Scala是一门多范式编程语言,混合了 面向对象编程 和 函数式编程 的风格。
在大数据处理中为什么要函数式编程?
函数式编程的一个重要特性就是值不可变性,这对于编写可扩展的并发程序而言可以带来巨大好处。因为它避免了对公共的可变状态进行同步访问控制的复杂问题。简单地说,我们完全不需要进行传统并发编程里面的同步、加锁等操作。
函数字面量
在面向过程和面向对象编程中,数据包括:类型 + 值。在函数式编程中,函数也可以跟其他数据类型一样,进行传递和操作。函数的使用跟其他数据类型没什么区别。函数也包括 类型 + 值。函数的值,就是函数字面量。
函数的类型
考虑下面的例子
1 | def counter(value: Int): Int = { |
输入一个 Int, 输出一个 Int ,因此这个函数的类型是:Int => Int
函数的值
把函数的类型声明部分(def counter)去掉,剩下的就是函数的值了。
在上面的例子中,输入 value, 输出 value+1 ,因此这个函数的值是:
1 | (value) => {value += 1} |
用函数式的方法定义函数
定义一个 Int 类型变量,可以:
1 | val num: Int = 5 |
有了函数式编程的思想后,定义一个函数,可以:
1 | val counter: Int => Int = { (value) => value += 1} |
- 我们定义了一个 counter 函数。
- 这个函数的类型是
Int => Int
,输入一个 Int,输出一个 Int - 这个函数的值是
value => value +=1
,输入一个 value,输出 value + 1
Lambda表达式与闭包
像 (参数) => 表达式
这样的定义形式,我们称之为 Lambda 表达式。其实就是用函数式的方法定义函数。例如:
1 | val myNumFunc: Int => Int = (num: Int) => num * 2 |
Scala有类型推断,因此可以简化为:
1 | // 省略前面类型 |
闭包
闭包是一种比较特殊的函数。闭包会引用函数外部的变量。
vczh:闭包不是“封闭内部状态”,而是“封闭外部状态”。一个函数如何能封闭外部状态呢?当外部状态的 scope 失效的时候,还有一份留在内部状态里面。
例如,定义一个函数:
1 | val addMore = (x: Int) => x + more |
变量more
并不是在函数内部定义的,而是函数外部的变量。
闭包(是一个函数)对捕获的外部变量做出的改变,在闭包之外是可见的!
高阶函数
一个接受其他函数作为参数 或者 返回一个函数 的函数就是高阶函数。
以求和函数为例:
不使用高阶函数
1 | def sumInts(a: Int, b: Int): Int = { |
使用高阶函数
1 | // 求和函数,传入一个函数、两个 Int |
sum 函数的参数是 (Int=>Int, Int, Int)
, 结果是 Int
, 因此, sum 函数的类型是:
1 | (Int=>Int, Int, Int) => Int |
也就是说,函数sum是一个接受函数参数的函数,因此,是一个高阶函数。
占位符语法
当每个参数在函数中最多出现一次时,可以使用 _
占位符来表示一个或多个参数。
1 | object Run { |
list2 和 list3 完全一样。也就是说,在这个例子中,x => x
和 _
完全一样。即把 List 中的每一个元素作为 x 传入。
Scala内置的高阶函数
map
map 对集合中的每个元素进行操作。
1 | val numberList = List(-3, -5, 1, 9, 6, -99) |
输出: List(-2, -4, 2, 10, 7, -98)
flatMap
flatMap是map的一种扩展。跟 map 不同的是, flatMap 会将子集合进行合并。
1 | val books = List("haddop", "spark", "scala") |
输出:List(List(h, a, d, d, o, p), List(s, p, a, r, k), List(s, c, a, l, a))
1 | val books = List("haddop", "spark", "scala") |
输出:List(h, a, d, d, o, p, s, p, a, r, k, s, c, a, l, a)
filter
遍历一个集合,并从中获取满足指定条件的元素组成一个新的集合。
reduce
reduce函数能把结果继续和序列的下一个元素做累积计算。结果就是计算的结果。
1 | val numberList = List(-3, -5, 1, 9, 6, -99) |
输出:-91
reduceLeft 和 reduceRight
1 | val a = List(1,7,2,9) |
fold
fold意为折叠,跟reduce类似。只不过 fold 提供了一个种子值,集合的第一个元素首先会跟种子值进行运算。
1 | val numberList = List(1, 9, 6) |
输出:20