大数据开发成长之路——Scala程序设计(二)

函数式编程

  • 稀里糊涂的学往往就成了死记硬背,可以参考一下知乎的这篇文章大致理解一下什么是函数式编程,它的优势在哪里,以及一些主要的特性
  • 我们可以将这里的函数理解为表达式,更侧重数学的推演过程,所以我们会看到scala中if/else会有返回值,需要一个var/val接收
  • 为了方便查看每一行代码的运行结果,推荐使用scala的REPL交互式解释器,可以即时编译、运行代码并返回结果,方便前期学习和测试
  • REPL:R(read)、E(evaluate) 、P(print)、L(loop),使用方法:
    在这里插入图片描述
    如果是方法体回车即可换行,使用ctrl+d结束输入
    :quit退出交互式解释器
    为了方便展示后面的代码块还是使用IDEA方式

  • 使用Spark/Flink框架的大量业务代码都会使用到函数式编程
  • 主要是结合List的一些操作

遍历 - foreach

  • 主要作用是对集合元素遍历查看

方法描述:foreach(f: (A) ⇒ Unit): Unit
接收一个函数对象,函数为匿名函数,输入参数为集合的元素

	val list = ListBuffer(1,2,3,4)
	list.foreach((x:Int)=>println(x))
	list.foreach(x=>println(x))	// 参数类型可省略
	list.foreach(println(_)) 	// 当函数参数,只在函数体中出现一次且没有嵌套调用

映射 - Map

  • 集合的映射操作是将来在编写Spark/Flink用得最多的操作
  • map的主要作用是对列表的每个元素执行自定义函数操作,返回B类型的集合(列表)

方法定义:def map[B](f: (A) ⇒ B): TraversableOnce[B]
B:B类型的集合,指定map方法最终返回的集合泛型

	println(list.map((x:Int)=>x*10))	// ListBuffer(10, 20, 30, 40)
	list.map(x=>x*10)
	list.map(_*10)
  • 扁平化映射 - flatMap

方法定义:def flatMap[B](f: (A) ⇒ GenTraversableOnce[B]): TraversableOnce[B]
该方法其本质是先进行了map 然后又调用了flatten

	val list1 = List("hadoop hive spark flink", "hbase spark")// 返回列表
    println(list1.flatMap(x => x.split(" ")))// List(hadoop, hive, spark, flink, hbase, spark)

过滤 - filter

方法定义:def filter(p: (A) ⇒ Boolean): TraversableOnce[A]
返回值:列表

	val list = ListBuffer(1,2,3,4,5,6)
	println(list.filter(x => x >5)) // ListBuffer(6)
	println(list.filter(_ > 5).map(_ * 10)) // ListBuffer(60)

排序 - sort

  • sorted默认排序
  • sortBy指定字段排序
  • sortWith自定义排序
	val list = ListBuffer(3,2,1,4,5,6)
	println(list.sorted)
	
	val list1=List("3 hadoop","9 spark","4 flink")
    println(list1.sortBy(x=>x.split(" ")(0))) // List(3 hadoop, 4 flink, 9 spark)
	
	println(list.sortWith((x,y)=>x>y))  // 降序
	println(list.sortWith((x,y)=>y<x))	// 降序
	println(list.sortWith((x,y)=>y>x))	// 升序
    println(list.sortWith((x,y)=>x<y))  // 升序 ListBuffer(1, 2, 3, 4, 5, 6)
    println(list.sortWith(_<_))		// 简写,够简吧!
    // 即传入比较大小的函数对象,函数体返回Boolean,为真则不改变参数顺序

分组 - groupBy

方法定义:def groupBy[K](f: (A) ⇒ K): Map[K, List[A]]
返回值:Map[K, List[A]] K为分组字段,List为这个分组字段对应的一组数据

	val list = List("张三"->"男", "李四"->"女", "王五"->"男")
    println(list.groupBy((kv:(String,String)) => {kv._2}))	// Map(男 -> List((张三,男), (王五,男)), 女 -> List((李四,女)))
    println(list.groupBy(_._2))	// 参数只出现一次,函数没有嵌套!丢掉传参,下划线代替,简写就行...
  • 初学的你可能好奇这个list为什么是map的定义方式,别慌,这是一种元素类型“Key->Value”,并非map专有!
	println(list.groupBy(_._2).map(x => x._1 -> x._2.size))// Map(男 -> 2, 女 -> 1)
	// 注:键值对的索引从1开始

聚合 - reduce

  • reduce表示将列表,传入一个函数进行聚合计算
	val m = List(1,2,3,4,5,6,7,8,9,10)
    println(m.reduce((x,y) => x + y))   // 累加,相当于m.sum=55
    println(m.reduce(_ + _))  // 简写
    println(m.reduceLeft(_ + _))	// 从左侧开始累加
    println(m.reduceRight(_ + _))	// 从右侧开始累加

折叠 - fold

  • fold与reduce相比,多了一个指定初始值参数
  • 最终折叠为一个元素
	val m = List(1,2,3,4,5,6,7,8,9,10)
	println(m.fold(100)((x,y)=>x+y))   // 初始值为100
	println(m.fold(100)(_+_))
	println(m.foldLeft(100)((x,y)=>x+y))
	println(m.foldRight(100)((x,y)=>x+y))

上面介绍的这些都是函数式编程的常见操作,用到的这些内置方法、函数表达式的运用都要熟练掌握,逐渐培养这样的编程思维!

高阶函数

  • 使用函数值作为参数,或者返回值为函数值的“函数”和“方法”,均称之为“高阶函数”
	val func=(x:Int)=>x*10
    val array=Array(1,2,3,4,5)
    array.map(func)		// 函数作为参数
  • 主要掌握以下几个概念
  1. 柯里化

方法可以定义多个参数列表,当调用多参数列表的方法时,内部会产生一个新的函数,该函数接收剩余的参数列表作为其参数

	def getAddress(a:String)(b:String,c:String):String={
    	a+"-"+b+"-"+c
  	}
  	println(getAddress("china")("beijing","tiananmen"))	// china-beijing-tiananmen
  	// 按照普通的写法应该是:柯里化会自动处理这个匿名函数为新函数
  	def getAddress(a:String):(String,String)=>String={
    	(b:String,c:String)=>a+"-"+b+"-"+c	// 最后一行是方法的返回值,这里返回String
	}

总结:意义在于把多参数列表的function等价转化成多个单参数列表的function的级联,这样所有的函数就都统一了,方便做lambda演算

  1. 闭包

函数里面引用外部变量叫作闭包
spark和flink程序的开发中大量的使用到函数,函数的返回值依赖的变量可能都需要进行大量的网络传输获取得到。这里就需要这些变量实现序列化进行网络传输
这也是函数作为变量表达式的优势所在,其他变量不在函数的作用域也可以调用

	// 简单来说
	var factor=10
	val f1=(x:Int) => x*factor	// 函数内部调用外部的参数

	// 进阶版
	def multi(x:Int) = (y:Int) => x*y	// 方法的返回值是一个函数,使用val接收
    val doubleFunc = multi(2)			// 作用:根据参数得到不同功能的函数
    val tribleFunc = multi(3)
    println(doubleFunc(10))   // 20
    println(tribleFunc(10))   // 30

总结:内部可以调用外部

结语

上面是scala中函数式编程的重点,深入的理解需要结合更多的数学知识,在大数据开发的学习过程中会逐步积累。
入门中的小白,如有不当之处烦请指出!
下一节介绍scala面向对象编程
在这里插入图片描述

发布了4 篇原创文章 · 获赞 14 · 访问量 3040

猜你喜欢

转载自blog.csdn.net/weixin_39757637/article/details/104838432