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面向對象編程
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章