用法一、簡寫函數字面量(function literal)
例子1:
val conf = new SparkConf()
conf.setMaster("local[2]").setAppName("")
val sc = new SparkContext(conf)
// sc.textFile("").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).foreach(println())
val lines: RDD[String] = sc.textFile("")
val words: RDD[String] = lines.flatMap(line => {
line.split(" ")
})
val pairwords: RDD[(String, Int)] = words.map(word => {
new Tuple2(word, 1) //new Tuple可以省略
})
val result = pairwords.reduceByKey((v1: Int, v2: Int) => {
v1 + v2
})
result.foreach(tuple => {
println(tuple)
})
sc.stop()
簡化後:
val conf = new SparkConf()
conf.setMaster("local[2]").setAppName("")
val sc = new SparkContext(conf)
sc.textFile("").flatMap(_.split(" ")).map((_,1)).reduceByKey(_+_).foreach(println())
sc.stop()
在flatMap、map、reduceByKey中傳入的匿名函數只使用了一次,例如:line => { line.split(" "),所以可以用下劃線代替,簡寫爲:_.split(" ")。
例子2:
val f1 = (_: Int) + (_: Int)
//等價於
val f2 = (x: Int, y: Int) => x + y
list.foreach(println(_))
//等價於
list.foreach(e => println(e))
list.filter(_ > 0)
//等價於
list.filter(x => x > 0)
其餘用法:從https://my.oschina.net/joymufeng/blog/863823這篇文章轉載而來,作者:joymufeng
下劃線這個符號幾乎貫穿了任何一本Scala編程書籍,並且在不同的場景下具有不同的含義,繞暈了不少初學者。正因如此,下劃線這個特殊符號無形中增加Scala的入門難度。本文希望幫助初學者踏平這個小山坡。
1. 用於替換Java的等價語法
由於大部分的Java關鍵字在Scala中擁有了新的含義,所以一些基本的語法在Scala中稍有變化。
1.1 導入通配符
*在Scala中是合法的方法名,所以導入包時要使用_代替。
//Java
import java.util.*;
//Scala
import java.util._
1.2 類成員默認值
Java中類成員可以不賦初始值,編譯器會自動幫你設置一個合適的初始值:
class Foo{
//String類型的默認值爲null
String s;
}
而在Scala中必須要顯式指定,如果你比較懶,可以用_讓編譯器自動幫你設置初始值:
class Foo{
//String類型的默認值爲null
var s: String = _
}
該語法只適用於類成員,而不適用於局部變量。
1.3 可變參數
Java聲明可變參數如下:
public static void printArgs(String ... args){
for(Object elem: args){
System.out.println(elem + " ");
}
}
調用方法如下:
//傳入兩個參數
printArgs("a", "b");
//也可以傳入一個數組
printArgs(new String[]{"a", "b"});
在Java中可以直接將數組傳給printArgs方法,但是在Scala中,你必須要明確的告訴編譯器,你是想將集合作爲一個獨立的參數傳進去,還是想將集合的元素傳進去。如果是後者則要藉助下劃線:
printArgs(List("a", "b"): _*)
1.4 類型通配符
Java的泛型系統有一個通配符類型,例如List<?>,任意的List<T>類型都是List<?>的子類型,如果我們想編寫一個可以打印所有List類型元素的方法,可以如下聲明:
public static void printList(List<?> list){
for(Object elem: list){
System.out.println(elem + " ");
}
}
對應的Scala版本爲:
def printList(list: List[_]): Unit ={
list.foreach(elem => println(elem + " "))
}
2 模式匹配
2.1 默認匹配
str match{
case "1" => println("match 1")
case _ => println("match default")
}
2.2 匹配集合元素
//匹配以0開頭,長度爲三的列表
expr match {
case List(0, _, _) => println("found it")
case _ =>
}
//匹配以0開頭,長度任意的列表
expr match {
case List(0, _*) => println("found it")
case _ =>
}
//匹配元組元素
expr match {
case (0, _) => println("found it")
case _ =>
}
//將首元素賦值給head變量
val List(head, _*) = List("a")
3. Scala特有語法
3.1 訪問Tuple元素
val t = (1, 2, 3)
println(t._1, t._2, t._3)
3.2 簡寫函數字面量(function literal)
如果函數的參數在函數體內只出現一次,則可以使用下劃線代替:
val f1 = (_: Int) + (_: Int)
//等價於
val f2 = (x: Int, y: Int) => x + y
list.foreach(println(_))
//等價於
list.foreach(e => println(e))
list.filter(_ > 0)
//等價於
list.filter(x => x > 0)
3.3 定義一元操作符
在Scala中,操作符其實就是方法,例如1 + 1等價於1.+(1),利用下劃線我們可以定義自己的左置操作符,例如Scala中的負數就是用左置操作符實現的:
-2
//等價於
2.unary_-
3.4 定義賦值操作符
我們通過下劃線實現賦值操作符,從而可以精確地控制賦值過程:
class Foo {
def name = { "foo" }
def name_=(str: String) {
println("set name " + str)
}
val m = new Foo()
m.name = "Foo" //等價於: m.name_=("Foo")
3.5 定義部分應用函數(partially applied function)
我們可以爲某個函數只提供部分參數進行調用,返回的結果是一個新的函數,即部分應用函數。因爲只提供了部分參數,所以部分應用函數也因此而得名。
def sum(a: Int, b: Int, c: Int) = a + b + c
val b = sum(1, _: Int, 3)
b: Int => Int = <function1>
b(2) //6
3.6 將方法轉換成函數
Scala中方法和函數是兩個不同的概念,方法無法作爲參數進行傳遞,也無法賦值給變量,但是函數是可以的。在Scala中,利用下劃線可以將方法轉換成函數:
//將println方法轉換成函數,並賦值給p
val p = println _
//p: (Any) => Unit
4. 小結
下劃線在大部分的應用場景中是以語法糖的形式出現的,可以減少擊鍵次數,並且代碼顯得更加簡潔。但是對於不熟悉下劃線的同學閱讀起來稍顯困難,希望通過本文能夠幫你解決這個的困惑。本文成文倉促,如有遺漏,歡迎留言! 轉載請註明作者: joymufeng
© 著作權歸作者所有