scala函数值编程之常用函数
面向函数式编程
- 面向函数式编程是一种编程范式。
- 可以简单的把面向函数式编程理解为:把函数作为参数传递到方法中,该函数会作用在集合中元素上,最后函数体来控制最终的业务逻辑。
遍历 - foreach
方法描述
foreach(f:(A)=>Unit):Unit
方法说明
foreach | API | 说明 |
---|---|---|
参数 | f: (A) ⇒ Unit | 接收一个函数对象 函数的输入参数为集合的元素 返回值为空 |
返回值 | Unit | 空 |
详细释义
- foreach()的参数类型f是个匿名函数:(A)=>Unit,foreach的返回类型是Unit,即第二个Unit
- 匿名函数(A)=>Unit的参数列表是(A),在
=>
右侧的函数体中处理业务逻辑。最后放回一个空值,即第一个Unit是匿名函数的返回类型(空) - A是foreach中每个元素的类型,每个元素A都经过函数处理。
- 调用foreach的集合决定A的类型。如:list.foreach,则A的数据类型和list中的元素数据类型一致
示例
scala> val list= List(1,2,3,4)
list: List[Int] = List(1, 2, 3, 4)
//定义一个匿名函数传入foreach中
scala> list.foreach((x:Int)=>println(x))
1
2
3
4
//scala支持类型推断,省略类型Int
scala> list.foreach((x)=>println(x))
1
2
3
4
//当函数参数,只在函数体重出现一次,而且函数体没有嵌套调用时,可以使用下划线来简化函数定义
scala> list.foreach(println(_))
1
2
3
4
//最简写
scala> list.foreach(println)
1
2
3
4
映射 - map
方法描述
def map[B](f:(A)=>B):TraversableOnce[B]
方法说明
map方法 | API | 说明 |
---|---|---|
泛型 | [B] | 指定map方法最终返回的集合泛型 |
参数 | f: (A) ⇒ B | 传入一个函数对象 该函数接收一个类型A(要转换的列表元素) 返回值为类型B |
返回值 | TraversableOnce[B] | B类型的集合 |
详细释义
- B是map方法的参数类型,map中的每一个函数经过匿名函数f处理之后,返回B类型的的集合,即TraversableOnce[B]
- 匿名参数f的参数类型为A,匿名参数f的返回类型为B
示例
scala> val list= List(1,2,3,4)
list: List[Int] = List(1, 2, 3, 4)
//定义一个匿名函数。匿名函数参数为Int类型,返回值为x*10的类型。(x*10为函数体最后一行,x*10的类型即为函数体返回的类型)
scala> list.map((x:Int)=>x*10)
res5: List[Int] = List(10, 20, 30, 40)
//scala支持类型腿短,省略参数类型
scala> list.map(x=>x*10)
res6: List[Int] = List(10, 20, 30, 40)
//参数x只在函数体中出现一次,且函数体没有嵌套调用,用_来简化定义
scala> list.map(_*10)
res7: List[Int] = List(10, 20, 30, 40)
扁平化映射 - flatmap
方法描述
def flatMap[B](f:(A)=>GenTraversableOnce[B]):TraversableOnce[B]
方法说明
flatmap方法 | API | 说明 |
---|---|---|
泛型 | [B] | 最终要转换的集合元素类型 |
参数 | f: (A) ⇒ GenTraversableOnce[B] | 传入一个函数对象 函数的参数是集合的元素 函数的返回值是一个集合 |
返回值 | TraversableOnce[B] | B类型的集合 |
详细释义
- 匿名函数f通过函数体处理调用flatmap中的每一个元素,最处理一个元素生成一个子集合,最终将所有子集合中的元素抽取出来组成一个大的新的集合
- flatmap实际是将原集合先调用map,然后调用flatten
示例
scala> val list=List("hello world","hadoop spark hive")
list: List[String] = List(hello world, hadoop spark hive)
//使用flatMap进行扁平化处理,得到其中的所有子集合的所有元素
scala> list.flatMap(x=>x.split(" "))
res9: List[String] = List(hello, world, hadoop, spark, hive)
//函数体重使用一个x并且没有嵌套函数,用_简化
scala> list.flatMap(_.split(" "))
res10: List[String] = List(hello, world, hadoop, spark, hive)
//flatMap的本质是先调用map后调用flatten
scala> list.map(_.split(" ")).flatten
res11: List[String] = List(hello, world, hadoop, spark, hive)
//调用map生成新的List,List中的每个元素是个数组
scala> val m = list.map(_.split(" "))
m: List[Array[String]] = List(Array(hello, world), Array(hadoop, spark, hive))
//对新的List进行扁平化处理,抽取每个子集合的元素生成一个新的String集合
scala> m.flatten
res12: List[String] = List(hello, world, hadoop, spark, hive)
过滤 - filter
过滤符合一定条件的元素
方法描述
def filter(p:(A)=>Boolean):TraversableOnce[A]
方法说明
filter方法 | API | 说明 |
---|---|---|
参数 | p: (A) ⇒ Boolean | 传入一个函数对象 接收一个集合类型的参数 返回布尔类型,满足条件返回true, 不满足返回false |
返回值 | TraversableOnce[A] | 列表 |
详细释义
- 只有经过P处理后的结果为true的元素才会被保留下来
- 最终所有被保留下来的元素进入TraversableOnce[A],TraversableOnce[A]集合作为filter的返回值
示例
scala> val list=List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)
//过滤出小于3的元素
scala> list.filter(_<3)
res13: List[Int] = List(1, 2)
//过滤出小于3的元素后,每个元素*10生成新的集合
scala> list.filter(_<3).map(_*10)
res14: List[Int] = List(10, 20)
排序 - sort
scala集合中支持以下几种排序
- sorted 默认排序,升序
- sortedBy 按照的指定关键字排序,默认升序
- sortWith 自定义排序
默认排序-sorted
示例
scala> val list=List(5,1,2,4,3)
list: List[Int] = List(5, 1, 2, 4, 3)
//默认就是升序
scala> list.sorted
res30: List[Int] = List(1, 2, 3, 4, 5)
按照指定关键字排序 sortBy
根据传入的函数转换后,再进行排序。默认升序
方法描述
def sortedBy[B](f:(A)=>B):List[A]
方法说明
sortBy方法 | API | 说明 |
---|---|---|
泛型 | [B] | 按照什么类型来进行排序 |
参数 | f: (A) ⇒ B | 传入函数对象 接收一个集合类型的元素参数 返回B类型的元素进行排序 |
返回值 | List[A] | 返回排序后的列表 |
示例
scala> val list = List("1 cTest","2 aTest","3 bTest","4 aaTest")
list: List[String] = List(1 cTest, 2 aTest, 3 bTest, 4 aaTest)
//按照字符串进行排序,默认升序
scala> list.sortBy(x=>x.split(" ")(1))
res16: List[String] = List(2 aTest, 4 aaTest, 3 bTest, 1 cTest)
sortWith
自定义排序,根据一个函数来进行自定义排序
方法描述
def sortWith(lt:(A,A)=>Boolen):List[A]
方法说明
sortWith方法 | API | 说明 |
---|---|---|
参数 | lt: (A, A) ⇒ Boolean | 传入一个比较大小的函数对象 接收两个集合类型的元素参数 返回两个元素大小,小于返回true,大于返回false |
返回值 | List[A] | 返回排序后的列表 |
详细释义
- lt的返回结果是true,则保持元素原位置不变
- lt的返回结果是false,则调换原来的元素位置
示例
scala> val list = List(2,3,1,6,4,5)
list: List[Int] = List(2, 3, 1, 6, 4, 5)
//降薪排列
scala> list.sortWith((x,y)=>(x>y))
res17: List[Int] = List(6, 5, 4, 3, 2, 1)
//升序排列
scala> list.sortWith((x,y)=>(x<y))
res18: List[Int] = List(1, 2, 3, 4, 5, 6)
//x和y都只在函数体中出现了一次,且没有嵌套函数,用_简写
scala> list.sortWith(_<_)
res19: List[Int] = List(1, 2, 3, 4, 5, 6)
分组 - groupBy
- groupBy表示按照函数将列表分成不同的组
- 一般在需要将数据按照分组来进行统计分析时,会用到分组方法group by
方法描述
def groupBy[K](f:(a)=>k):Map[K,List[A]]
方法说明
groupBy方法 | API | 说明 |
---|---|---|
泛型 | [K] | 分组字段的类型 |
参数 | f: (A) ⇒ K | 传入一个函数对象 接收集合元素类型的参数 返回一个K类型的key,这个key会用来进行分组,相同的key放在一组中 |
返回值 | Map[K, List[A]] | 返回一个映射,K为分组字段,List为这个分组字段对应的一组数据 |
详细释义
- 每次传递进来的K值相同则分到同一组,分组依据是匿名函数f
- 返回值是个Map,K是原集合笔筒的K值的集合,list为每个K对应的自己的一组数据
示例
scala> val a = List("张三"->"男", "李四"->"女", "王五"->"男")
a: List[(String, String)] = List((张三,男), (李四,女), (王五,男))
//按照性别分组
scala> a.groupBy((kv:(String,String))=>{kv._2})
res2: scala.collection.immutable.Map[String,List[(String, String)]] = Map(男 -> List((张三,男), (王五,男)), 女 -> List((李四,女)))
//scala支持自动推断,省略类型
scala> a.groupBy(kv=>{kv._2})
res3: scala.collection.immutable.Map[String,List[(String, String)]] = Map(男 -> List((张三,男), (王五,男)), 女 -> List((李四,女)))
//kv在函数体中仅出现一次,且没有嵌套函数,使用_简写
scala> a.groupBy(_._2)
res4: scala.collection.immutable.Map[String,List[(String, String)]] = Map(男 -> List((张三,男), (王五,男)), 女 -> List((李四,女)))
//将分组后的映射转换为性别->人数 的元组列表
scala> res4.map(x=>x._1->x._2.size)
res5: scala.collection.immutable.Map[String,Int] = Map(男 -> 2, 女 -> 1)
示例2
scala> val a =List("zhangSan"->10,"lisi"->10,"wangwu"->20)
a: List[(String, Int)] = List((zhangSan,10), (lisi,10), (wangwu,20))
//按照list的元素的第二个元素分组
scala> a.groupBy((kv:(String,Int))=>{kv._2})
res6: scala.collection.immutable.Map[Int,List[(String, Int)]] = Map(20 -> List((wangwu,20)), 10 -> List((zhangSan,10), (lisi,10)))
//按照List的元素的第一个元素分组
scala> a.groupBy((kv:(String,Int))=>{kv._1})
res7: scala.collection.immutable.Map[String,List[(String, Int)]] = Map(zhangSan -> List((zhangSan,10)), lisi -> List((lisi,10)), wangwu -> List((wangwu,20)))
聚合 - reduce
- reduce表示将列表传入一个函数进行聚合计算
- reduce默认调用reduceLeft
- reduce底层实际调用的是递归函数
scala2.11.2源码
def reduce[A1 >: A](op: (A1, A1) => A1): A1 = reduceLeft(op)
/** Applies a binary operator to all elements of this $coll,
* going left to right.
* $willNotTerminateInf
* $orderDependentFold
*
* @param op the binary operator.
* @tparam B the result type of the binary operator.
* @return the result of inserting `op` between consecutive elements of this $coll,
* going left to right:
* {{{
* op( op( ... op(x_1, x_2) ..., x_{n-1}), x_n)
* }}}
* where `x,,1,,, ..., x,,n,,` are the elements of this $coll.
* @throws UnsupportedOperationException if this $coll is empty. */
def reduceLeft[B >: A](op: (B, A) => B): B = {
if (isEmpty)
throw new UnsupportedOperationException("empty.reduceLeft")
var first = true
var acc: B = 0.asInstanceOf[B]
for (x <- self) {
if (first) {
acc = x
first = false
}
else acc = op(acc, x)
}
acc
}
方法描述
def reduce[A1 >:A](op:(A1,A1) => A1):A1
方法说明
reduce方法 | API | 说明 |
---|---|---|
泛型 | [A1 >: A] | (下界)A1必须是集合元素类型的子类 |
参数 | op: (A1, A1) ⇒ A1 | 传入函数对象,用来不断进行聚合操作 第一个A1类型参数为:当前聚合后的变量 第二个A1类型参数为:当前要进行聚合的元素 |
返回值 | A1 | 列表最终聚合为一个元素 |
详细释义
- 第一次遍历:集合中第一个元素赋值给第一个A1,第二个元素赋值给第二个A1
- 第二次遍历:将两个A1元素匿名函数op处理后的结果赋给第一个A1,将集合中的第三个元素赋值给第二个A2
- 重复执行上面两步,直到处理完整个集合的元素。
- 第一个下划线表示第一个参数,就是历史的聚合数据结果
- 第二个下划线表示第二个参数,就是当前准备要聚合的数据元素
示例
scala> val a = List(1,2,3,4,5,6,7,8,9,10)
a: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
//集合中第一个元素赋给x,第二个赋给y
//相加后的元素赋给x,第三个元素赋给y
//以此类推
scala> a.reduce((x:Int,y:Int)=>x+y)
res8: Int = 55
// 第一个下划线表示第一个参数,就是历史的聚合数据结果
// 第二个下划线表示第二个参数,就是当前要聚合的数据元素
scala> a.reduce((x,y)=>x+y)
res9: Int = 55
//x,y都在函数体中出现一次,且没有嵌套函数。使用_简化写法。另外,由于有不止一个变量,所以将_,_._+_写成_._
scala> a.reduce(_+_)
res12: Int = 55
//从左侧开始聚合
scala> a.reduceLeft(_+_)
res13: Int = 55
//从右侧开始聚合
scala> a.reduceRight(_+_)
res14: Int = 55
折叠 - fold
- fold类似reduce,不过前面指定了一个初始值参数
- fold默认调用foldLeft
- 实际上这是一种柯里化写法
scala2.11.2源码
def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
def foldLeft[B](z: B)(op: (B, A) => B): B = {
var result = z
this foreach (x => result = op(result, x))
result
}
方法值描述
def fold[A1>: A](z:A1)(op:(A1,A1)=>A1):A1
方法说明
More Actionsreduce方法API说明泛型[A1 >: A](下界)A1必须是集合元素类型的子类参数1z: A1初始值参数2op: (A1, A1) ⇒ A1传入函数对象,用来不断进行折叠操作
第一个A1类型参数为:当前折叠后的变量
第二个A1类型参数为:当前要进行折叠的元素返回值A1列表最终折叠为一个元素
示例
scala> val a = List(1,2,3,4,5,6,7,8,9,10)
a: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
//给定初始值0,对list进行累加操作
scala> a.fold(0)(_+_)
res19: Int = 55
//给定初始值10,对List进行累加操作
scala> a.fold(10)(_+_)
res20: Int = 65
//给定初始值10,从左侧对开始对list进行折叠
scala> a.foldLeft(10)(_+_)
res21: Int = 65
//给定初始值10,从右侧开始对list进行折叠
scala> a.foldRight(10)(_+_)
res22: Int = 65
Reduce和Fold区别
- 初始值
- Reduce初始值是集合元素的头或尾(left或者right)
- Fold必须手动指定初始值
- 空集合操作
- Reduce进行空集合操作会抛异常
- Fold进行空集合操作结果为初始值
- 返回值
- Reduce最终聚合的元素必须和调用Reduce方法的集合中元素一致
- Fold最终折叠得到的元素类型可以和调用Fold中的元素不一致(使用FoldLeft)
- 底层实现
- Reduce底层采用递归实现
- Fold底层采用foreach实现