Spark(七) -- Scala快速入門
Scala作為Spark的開發語言,想要成為Spark高手,精通Scala是必須要走的一條路
然後一門語言並不是你想精通就能夠精通的,更何況是Scala這種面向物件又面向函式的程式語言,個人覺得其學習的門檻會比C#,Java等面嚮物件語言要高
所以,這篇文章是建立在有一點程式語言知識的基礎上的(如學過C#或者Java等),其實所有語言都是大同小異的,學會了一門語言在學其他的就不會像剛開始那麼吃力了,因為它們很多概念都是相通的
本篇文章主要是介紹Scala本身的一些特性,以便以能夠快速的上手開發,而對於真正要精通Scala,顯然要付出的努力還要很多
安裝Scala的開發環境就不具體介紹了,請自行百度之。IDE使用的是Eclipse For Scala,下載地址:
本文大概從以下幾個方面來介紹Scala:
- 宣告變數的關鍵字
- 方法的定義格式
- 條件表示式
- 預設引數,帶名引數和變長引數
- lazy變數
- 陣列
- Map操作
- 元組
- 值函式
- 匿名函式
- 函式柯里化
- 高階函式示例
- List序列
- case class和模式匹配
Scala中宣告變數的關鍵字只有兩種:
val和var,分別表示常量和變數的宣告(在Scala提倡中儘量使用val而不是var)
方法的定義格式:
def 方法名(引數名:引數型別,…):返回值型別={方法體}
如:
def add(x:Int,y:Int):Int={
x + y
}
定義了一個引數為兩個Int型別,返回值也為Int型別的方法add,返回x+y的值
相信已經有人注意到了,在Scala中,每個句子是不用以分號結束的(當然加上分號也沒事),而且方法中不用return來返回,預設返回的是最後一行的值
當方法沒有返回值時表現形式為:
def add():Unit={...}
或者直接省略:Unit
def add()={...}
在呼叫方法的時候,如果該方法沒有引數,可以省略括號,如:
add//加上括號也沒事,如add()
條件表示式:
和其他語言的if/else唯一的卻別就是,Scala的if/else是有返回值的
val x = 8
val res = if(x > 7) 1 else 0
如果x>7就返回1,否則返回0,賦值給res
Scala中的while和do-while用法是和其他語言一樣的,但是for的用法就不同了
for迴圈語句的格式為:
for(i <- 表示式){
迴圈體
}
例如:
for(i <- 1 to 10){
println(i)
}
上面程式碼中,1 to 10會產生1-10中的每個數,i會迭代迴圈這10個數,迭代一次執行一次迴圈體。
剛開始看肯定會很奇怪,<-這個符號是必須的,現在假設我們有一個arr陣列,裡面有1-10這10個數
首先用C#的迴圈方式為:
for(int i = 0;i < arr.Length;i++)
{
Console.WriteLine(arr[i]);
}
//或者foreach方式
foreach(var i in arr)
{
Console.WriteLine(i);
}
在對比一下Scala的for迴圈:
for(i <- 0 to arr.length){
println(arr[i])
}
//for同時也可以當foreach使用
for(i <- arr){
println(i)
}
並且,在Scala中,for迴圈是可以新增if判斷語句的,如:
for(i <- 0 to 10 if i % 2 == 0){
println(i)
}
上面程式碼的意思是,迴圈0-10,只打印出偶數
Scala的迴圈表示式中沒有continue和break語句,但是可以通過一個Boolean變數和巢狀函式中return來實現
預設引數,帶名引數和變長引數:
這幾個概念很簡單,通過幾行例項程式碼基本可以瞭解
預設引數:
def getName(name:String = "JChubby") = {...}
呼叫getName時如果沒有傳引數,則會使用預設的JChubby
帶名引數:
def showNum(x:Int,y:Int):Unit = {...}
呼叫該方法時,引數可以不按順序,但是要執行引數名:
showNum(y = 2,x = 4)
變長引數:
def canChangeLength(x:Int*) = {...}
呼叫時:
canChangeLength(1,2,3,4,...)
lazy變數:
正常情況下使用val或者var宣告變數時是直接分配記憶體空間使用的
當使用lazy關鍵字時,這個常/變數只有在使用的時候才會被分配記憶體
如在Scala命令列中輸入下面的程式碼之後回車:
lazy val a = 1
lazy(懶值)對應開銷很大的初始化操作非常有用,如在讀取一個很大的檔案的時候,如果一開始並不馬上使用,可以將其標記為lazy,直到檔案真正使用的時候才分配記憶體空間
陣列:
Scala中分為兩種:
定長陣列:scala.collection.immutable.Array,一旦宣告之後長度不可變
變長陣列:scala.collection.mutable.ArrayBuffer,動態陣列
定長陣列使用:
val arr = new Array[Int](3)//宣告長度為3,型別為Int的Array陣列(new可省略)
//或者宣告時直接賦值
//編譯器會自動推斷型別為Int
val arr = Array(1,2,3)
//通過下標訪問陣列的方式為()而不是[],請注意!
val a = arr(0)
變長陣列使用:
val arrBuf = scala.collection.mutable.ArrayBuffer[Int]()
//新增元素,相當於add
arrBuf += 1
//也可以一次性新增多個
arrBuf += (1,2,3)
//使用++=可以直接新增一個數組
arrBuf ++= Array(1,2,3)
//在指定位置插入若干個元素,在0位置插入1,2,3
arrBuf.insert(0,1,2,3)
//Scala中的函式除了通過.來呼叫之外也可以直接寫成一下形式
arrBuf insert (0,1,2,3)
//之前的1 to 10 其實就是呼叫了1.to(10)這個to方法
//移除指定位置的元素
arrBuf remove 0
//移除指定位置後的連續n個元素
arrBuf remove 0,3
//刪除末尾的n個元素
arrBuf trimEnd 3
此外還有例如min,max等函式求最大最小等常用操作
Map操作:
和陣列一樣,Map同樣也分為可變和不可變兩種
不可變Map的使用:
//宣告一個Map,包含兩個鍵值對,->表示鍵值對應的關係
val myMap = Map("JChubby" -> 22,"Looky" -> 21)
不可變的Map中,一旦宣告之後,該Map不可新增不可刪除不可修改,只能檢視,檢視方式與可變Map一致
可變Map的使用:
val myMap = scala.collection.mutable.Map("JChubby" -> 22,"Looky" -> 21)
//新增元素
myMap += ("abc" -> 12,"bcd" -> 13)
//刪除元素
myMap -= "abc"
//修改元素
myMap("JChubby") = 10
//或者
myMap += ("JChubby" -> 10)
//查詢元素
myMap("JChubby")
//或者使用getOrElse,該方法在取不到值的時候返回一個預設值,以免出現異常
myMap getOrElse ("JChubby",-1)
//遍歷,使用的是k,v格式來進行遍歷
for((k,v) <- myMap){
println(k + ":" + v)
}
//如果只要遍歷k或者v,可以使用_佔位符
for((k,_) <- myMap){
println(k)
}
//獲取所有的key
myMap.keySet
//獲取所有的value
myMap.values
元組:
元組其實就是一個集合,但是可以存放各種不同型別的資料
例如:
val group = (1,2,"JChubby",3.0)
//通過下標訪問元組的格式為:元組名._下標 或者 元組名 _下標。如:
group._0
group _0
//遍歷元組格式為,productIterator為固定格式不可少
for(element <- group.productIterator){
...
}
值函式:
Scala中可以將函式賦值給一個變數,這個變數就稱為值函式
def add(x:Int,y:Int):Int = {
x + y
}
//格式為方法名+空格+_
var res = add _
//之後可以將這個變數當做方法來用
res(1,2)
匿名函式:
顧名思義,沒有名字的函式,定義格式如下:
(引數名:引數型別,…) => 表示式
例如:
(x:Int) => x + 3
可以將匿名函式賦值給一個常量
val func = (x:Int) => x + 3
//直接呼叫常量
func(7)
上面的用法其實和直接定義一個有名的函式是一樣的,匿名函式主要是當做函式的引數來傳遞使用
例如scala.collection.mutable.ArrayBuffer的map方法,其引數要求是一個匿名函式
scala.collection.mutable.ArrayBuffer.map((x:Int) => x +3)
執行的結果是ArrayBuffer裡面每個值都加3(map函式之後會介紹)
函式柯里化:
函式柯里化就是將原本有 很多引數 的函式,分成一個個只有 一個引數 的函式,每個函式的返回值都是 一個表示式 並且當做引數 傳到下一個函式
例如:
def mul(x:Int,y:Int) = x + y
//呼叫方式為
mul(1,2)
//柯里化後的函式為
def mul(x:Int)(y:Int) = x + y
//呼叫當時為
mul(1)(2)
//執行mul(1)時,返回的是1 + y,在將這個函式應用到2,得到 1 + 2
柯里化是閉包的典型體現,具體柯里化的概念請百度之~
高階函式示例:
陣列的map函式
val arr = Array(1,2,3)
arr.map(1 + _)
_表示陣列中的每個元素,執行的結果為每個元素+1
陣列的filter函式
arr.filter(_ > 2)
_ > 2會將陣列中大於2的元素過濾出來
陣列的reduce函式
arr.reduce(_ + _)
reduce是迭代運算,將陣列中兩個元素先執行操作,把結果作為一個元素繼續和下一個元素進行操作,執行結果為:1+2=3,3+3=9,也就是求和
List序列:
List操作:
val list = List(1,2,3)
//獲取頭元素
list.head
//獲取除了頭元素之外的其他元素
list.tail
//新增元素
7::list
//注意,::運算子是從右向左的
//獲取前n個元素
list.take(n)
//list的zip操作
val list1 = List(a,b,c)
list.zip(list1)
//得到的結果為List((1,a),(2,b),(3,c)),如果兩個list的長度不同,以比較短的為基礎
//flatten操作
val list2 = List(List(1,2),List(2,3),List(3,4))
list2.flatten
//得到的結果為List(1,2,2,3,3,4),用於將巢狀List組合成一個新的List
//flatmap操作
list2.flatmap(_.map(_ * 2))
//跟flatten相比,flatmap在組合之前,會先對每個子List進行map操作
case class和模式匹配:
case class又稱為樣例類,和普通的類相比,樣例類不用new就可以例項化,預設執行的是apply方法來構造物件
模式匹配:
和其他語言的switch功能差不多,但是Scala裡面使用的是match
使用示例:
val res = 1
res match{
case 0 => println(0)
case 1 => println(1)
case 2 => println(2)
}
除了用法格式上略有不同,其餘的基本類似,並且不需要使用break關鍵字
樣例類和模式匹配結合使用:
//定義抽象類Human
abstract class Human
//分別定義用例類Chinese,Japanese,American都繼承自Human
case class Chinese(name:String) extends Human
case class Japanese(name:String) extends Human
case class American(name:String) extends Human
//定義模式匹配方法
def caseMatch(human:Human){
//human引數型別不同,做出不同處理,Chinese(_)表示只關心是不是Chinese而不關心叫什麼名字
case Chinese(_) => println("Chinese")
case Japanese(_) => println("Japanese")
case American(_) => println("American")
}