1 摘要
本章博客主要探討了,Scala語言中的下划線的用法。作為初學者,這個用法很隱晦。但在Scala中這種寫法會讓代碼變得簡介高效且容易修改,所以好好學習一下是非常有必要的。很好的參考材料
(https://stackoverflow.com/questions/8000903/what-are-all-the-uses-of-an-underscore-in-scala)
2 參數的占位符
Scala語言中,用(_)來表示一個函數的參數,注意:只有在你打算引用的這個函數值中只引用這個參數一次時可以這樣做,可以在一個函數值中多次使用下划線,但是每個下划線都表示相繼的不同的參數,下面有一些例子:
2.1、Placeholder syntax(占位符,函數參數的占位符)
正確的示范:
因為傳入的匿名函數只使用了一次,所以寫成:(1 to 9).map(_*2)
錯誤的示范:
如果要映射成x=>(x,x*x),比如:1->(1,1),2->(2,4)等於元素的集合,可以按照下面方式寫:(1 to 9).map(a=>(a,a*a)),如下寫成下面就是錯誤的示范
總結:傳入的匿名函數參數只使用了一次,就可以用下划線代替。其實這樣能讓代碼精簡,尤其實在傳入的匿名函數的參數值使用一次的時候,這個時候給這個變量命名就顯示不那么重要了,所以在合適的地方使用(_)。
2.2 Partially applied functions(偏應用函數)
調用一個函數,實際上是在一些參數上應用這些函數。如果傳遞了所有期望的參數,就是對這個函數的完整應用,如果傳遞的參數比所要求的參數少,就會得到另外一個函數,就被成為偏應用函數。
1 /** 2 * 3 * @param d 時間 4 * @param log 日志信息 5 */ 6 def showLog(d: Date, log: String): Unit = { 7 println("時間:" + d, ",日志信息:" + log) 8 } 9 val date = new Date() 10 //偏應用函數 11 showLog(date: Date, _: String)
下划線表示部分傳入參數。
2.3 參數路由
列舉了Spark Streaming中updateStateByKey算子的使用來說明(_)能表示整個參數列表
是一個將函數值變得簡單的方法,
1 def main(args: Array[String]): Unit = { 2 val sc = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount") 3 val ssc = new StreamingContext(sc, Duration(5000)) 4 ssc.checkpoint("./") 5 val lines = ssc.socketTextStream("39.96.7.174", 1884) 6 val result = lines.flatMap(_.split(" ")).map((_, 1)) 7 val state = result.updateStateByKey(updateFunction _) 8 state.print() 9 ssc.start() 10 ssc.awaitTermination() 11 } 12 13 def updateFunction(currentValue: Seq[Int], preValue: Option[Int]): Option[Int] = { 14 val sum = currentValue.sum 15 val pre = preValue.getOrElse(0) 16 Some(sum + pre) 17 }
spark中updatestateBykey算子的使用中,第七行表示:_表示傳入了整個參數列表,所以對updateFunction()的調用能寫入上面的方式。
3 spark 算子中
3.1 map、flatMap、reduceBykey簡寫
未簡化:
1 val conf = new SparkConf() 2 conf.setMaster("local[2]").setAppName("") 3 val sc = new SparkContext(conf) 4 // sc.textFile("").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).foreach(println()) 5 val lines: RDD[String] = sc.textFile("") 6 val words: RDD[String] = lines.flatMap(line => { 7 line.split(" ") 8 }) 9 val pairwords: RDD[(String, Int)] = words.map(word => { 10 new Tuple2(word, 1) //new Tuple可以省略 11 }) 12 val result = pairwords.reduceByKey((v1: Int, v2: Int) => { 13 v1 + v2 14 }) 15 result.foreach(tuple => { 16 println(tuple) 17 }) 18 sc.stop()
簡化后:
1 val conf = new SparkConf() 2 conf.setMaster("local[2]").setAppName("") 3 val sc = new SparkContext(conf) 4 sc.textFile("").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).foreach(println()) 5 sc.stop()
在flatMap、map、reduceByKey中傳入的匿名函數只使用了一次,例如:line => { line.split(" "),所以可以用下划線代替,簡寫為:_.split(" ")。
4 總結
總結用法如下:
- 作為包引入通配符。例如scala中import java.util._等同於Java中的import java.util.*
- 作為元祖索引的前綴。對於給定的元祖,可以用._1和._2來分別索引相應的值
- 作為函數的隱式參數。如:list.map{_*2}和list.map{e=>e*2}是等價的
- 用於默認值初始化變量。var min :Int=_將使用0初始化變量min。
- 用於在函數名中混合操作符。待補充,目前還沒遇到。
- 在進行模式匹配作為通配符。在使用模式匹配的時候,case_將會匹配任意給定的值,case_:Int將會匹配任何整數。
- 在處理異常時候,在catch代碼塊中和case聯用
- 作為分解操作的一部分。例如,max(args:_*)在將數組或者列表參數傳遞給接受可變長度參數的函數前,將其分解為離散的值
- 用於偏應用函數。前面有詳細介紹
注意調整Scala代碼到一個折中的程度,以達到對可讀性的要求,我們在讓Scala代碼簡潔的同時,不能讓代碼的含義變得模糊。