本篇繼續(xù)上一篇對Scala的整體介紹继找,本篇進(jìn)一步解釋Scala的一些高級特性,當(dāng)你學(xué)完本篇后,就有足夠的知識編寫一些實(shí)用的Scala腳本應(yīng)用了慌洪。
第七步:使用類型參數(shù)化數(shù)組
在Scala中你可以使用new來實(shí)例化一個類策严。當(dāng)你創(chuàng)建一個對象的實(shí)例時穗慕,你可以使用數(shù)值或類型參數(shù)。如果使用類型參數(shù)妻导,它的作用類似Java或.Net的Generic類型逛绵。所不同的是Scala使用方括號來指明數(shù)據(jù)類型參數(shù),而非尖括號倔韭。比如
1valgreetStrings=newArray[String](3)
2greetStrings(0)="Hello"
3greetStrings(1)=","
4greetStrings(2)="world!\n"
5for(i <-0to2)
6print(greetStrings(i))
可以看到Scala使用[]來為數(shù)組指明類型化參數(shù)暑脆,本例使用String類型,數(shù)組使用()而非[]來指明數(shù)組的索引狐肢。其中的for表達(dá)式中使用到 0 to 2 ,這個表達(dá)式演示了Scala的一個基本規(guī)則添吗,如果一個方法只有一個參數(shù),你可以不用括號和. 來調(diào)用這個方法份名。
因此這里的 0 to 2, 其實(shí)為(0).to(2) 調(diào)用的為整數(shù)類型的 to方法碟联,to方法使用一個參數(shù)。Scala中所有的基本數(shù)據(jù)類型也是對象(和Java不同)僵腺,因此0 可以有方法(實(shí)際上調(diào)用的是RichInt的方法)鲤孵,這種只有一個參數(shù)的方法可以使用操作符的寫法(不用.和括號),實(shí)際上Scala中表達(dá)式 1+2 ,最終解釋為(1).+(2) +也是Int的一個方法辰如,和Java不同的是普监,Scala對方法的名稱沒有太多的限制,你可以使用符合作為方法的名稱。
這里也說明為什么Scala中使用()來訪問數(shù)組元素凯正,在Scala中毙玻,數(shù)組和其它普遍的類定義一樣,沒有什么特別之處廊散,當(dāng)你在某個值后面使用()時桑滩,Scala將其翻譯成對應(yīng)對象的apply方法。因此本例中g(shù)reetStrings(1) 其實(shí)調(diào)用greetString.apply(1)方法允睹。這種表達(dá)方法不僅僅只限于數(shù)組运准,對于任何對象,如果在其后面使用(),都將調(diào)用該對象的apply方法缭受。同樣的如果對某個使用()的對象賦值胁澳,比如:
1greetStrings(0)="Hello"
Scala將這種賦值轉(zhuǎn)換為該對象的update 方法, 也就是 greetStrings.update(0,”hello”). 因此上面的例子米者,使用傳統(tǒng)的方法調(diào)用可以寫成:
1valgreetStrings=newArray[String](3)
2greetStrings.update(0,"Hello")
3greetStrings.update(1,",")
4greetStrings.update(2,"world!\n")
5for(i <-0to2)
6print(greetStrings.apply(i))
從這點(diǎn)來說听哭,數(shù)組在Scala中并不某種特殊的數(shù)據(jù)類型,和普通的類沒有什么不同塘雳。
不過Scala還是提供了初始化數(shù)組的簡單的方法陆盘,比如什么的例子數(shù)組可以使用如下代碼:
1valgreetStrings=Array("Hello",",","World\n")
這里使用()其實(shí)還是調(diào)用Array類的關(guān)聯(lián)對象Array的apply方法,也就是
1valgreetStrings=Array.apply("Hello",",","World\n")
第八步: 使用Lists
Scala也是一個面向函數(shù)的編程語言败明,面向函數(shù)的編程語言的一個特點(diǎn)是隘马,調(diào)用某個方法不應(yīng)該有任何副作用,參數(shù)一定妻顶,調(diào)用該方法后酸员,返回一定的結(jié)果,而不會去修改程序的其它狀態(tài)(副作用)讳嘱。這樣做的一個好處是方法和方法之間關(guān)聯(lián)性較小幔嗦,從而方法變得更可靠和重用性高。使用這個原則也就意味著就變量的設(shè)成不可修改的,這也就避免了多線程訪問的互鎖問題沥潭。
前面介紹的數(shù)組邀泉,它的元素是可以被修改的。如果需要使用不可以修改的序列钝鸽,Scala中提供了Lists類汇恤。和Java的List不同,Scala的Lists對象是不可修改的拔恰。它被設(shè)計(jì)用來滿足函數(shù)編程風(fēng)格的代碼因谎。它有點(diǎn)像Java的String,String也是不可以修改的颜懊,如果需要可以修改的String對像财岔,可以使用StringBuilder類风皿。
比如下面的代碼:
1valoneTwo=List(1,2)
2valthreeFour=List(3,4)
3valoneTwoThreeFour=oneTwo:::threeFour
4println (oneTwo +" and "+ threeFour +" were not mutated.")
5println ("Thus, "+ oneTwoThreeFour +" is a new list")
定義了兩個List對象oneTwo和threeFour ,然后通過:::操作符(其實(shí)為:::方法)將兩個列表鏈接起來。實(shí)際上由于List的不可以修改特性匠璧,Scala創(chuàng)建了一個新的List對象oneTwoThreeFour來保存兩個列表連接后的值桐款。
List也提供了一個::方法用來向List中添加一個元素,::方法(操作符)是右操作符患朱,也就是使用::右邊的對象來調(diào)用它的::方法,Scala中規(guī)定所有以:開頭的操作符都是右操作符炊苫,因此如果你自己定義以:開頭的方法(操作符)也是右操作符裁厅。
如下面使用常量創(chuàng)建一個列表:
1valoneTowThree=1::2::3::Nil
2println(oneTowThree)
調(diào)用空列表對象Nil的 ::方法 也就是
1valoneTowThree=Nil.::(3).::(2).::(1)
Scala 的List類還定義其它很多很有用的方法,比如 head, last,length, reverse,tail 等這里就不一一說明了侨艾,具體可以參考List的文檔执虹。
第九步:使用元組(Tuples)
Scala 中另外一個很有用的容器類為Tuples,和List不同的Tuples可以包含不同類型的數(shù)據(jù)唠梨,而List只能包含同類型的數(shù)據(jù)袋励。Tuples在方法需要返回多個結(jié)果時非常有用。(Tuple對應(yīng)到數(shù)學(xué)的矢量的概念)当叭。
一旦定義了一個元組茬故,可以使用._和索引來訪問員組的元素(矢量的分量,注意和數(shù)組不同的是蚁鳖,元組的索引從1開始)
1valpair=(99,"Luftballons")
2println(pair._1)
3println(pair._2)
元祖的實(shí)際類型取決于它的分量的類型磺芭,比如上面pair的類型實(shí)際為 Tuple2[Int,String],而 (‘u’,’r’,”the”,1,4,”me”)的類型為Tuple6[Char,Char,String,Int,Int,String].
目前Scala支持的元祖的最大長度為22.如果有需要,你可以自己擴(kuò)展更長的元祖醉箕。
第十步: 使用Sets和Maps
Scala語言的一個設(shè)計(jì)目標(biāo)是讓程序員可以同時利用面向?qū)ο蠛兔嫦蚝瘮?shù)的方法編寫代碼钾腺,因此它提供的集合類分成了可以修改的集合類和不可以修改的集合類兩大類型。比如Array總是可以修改內(nèi)容的讥裤,而List總是不可以修改內(nèi)容的放棒。類似的情況,Scala也提供了兩種Sets和Map集合類己英。
比如Scala API 定義了Set的基Trait類型Set(Trait的概念類似于Java中的Interface间螟,所不同的Scala中的Trait可以有方法的實(shí)現(xiàn)),分兩個包定義Mutable(可變)和Immutable(不可變)损肛,使用同樣名稱的子Trait寒亥。下圖為Trait和類的基礎(chǔ)關(guān)系:
使用Set的基本方法如下:
1varjetSet=Set ("Boeing","Airbus")
2jetSet +="Lear"
3println(jetSet.contains("Cessna"))
缺省情況Set為Immutable Set,如果你需要使用可修改的集合類(Set類型)荧关,你可以使用全路徑來指明Set溉奕,比如 scala.collection.mutalbe.Set 。
Scala提供的另外一個類型為Map類型忍啤,Scala也提供了Mutable和Immutable 兩種Map 類型
Map的基本用法如下(Map類似于其它語言中的關(guān)聯(lián)數(shù)組如PHP)
1valromanNumeral=Map (1->"I",2->"II",
23->"III",4->"IV",5->"V")
3println (romanNumeral(4))
第十一步: 學(xué)習(xí)識別函數(shù)編程風(fēng)格
Scala語言的一個特點(diǎn)是支持面向函數(shù)編程加勤,因此學(xué)習(xí)Scala的一個重要方面是改變之前的指令式編程思想(尤其是來自Java或C#背景的程序員)仙辟,觀念要想函數(shù)式編程轉(zhuǎn)變。首先在看代碼上要認(rèn)識哪種是指令編程鳄梅,哪種是函數(shù)式編程叠国。實(shí)現(xiàn)這種思想上的轉(zhuǎn)變,不僅僅會使你成為一個更好的Scala程序員戴尸,同時也會擴(kuò)展你的視野粟焊,使你成為一個更好的程序員。
一個簡單的原則孙蒙,如果代碼中含有var類型的變量项棠,這段代碼就是傳統(tǒng)的指令式編程,如果代碼只有val變量挎峦,這段代碼就很有可能是函數(shù)式代碼香追,因此學(xué)會函數(shù)式編程關(guān)鍵是不使用vars來編寫代碼。
來看一個簡單的例子:
1defprintArgs ( args:Array[String]):Unit={
2vari=0
3while(i < args.length) {
4println (args(i))
5i+=1
6}
7}
來自Java背景的程序員開始寫Scala代碼很有可能寫成上面的實(shí)現(xiàn)坦胶。我們試著去除vars變量透典,可以寫成跟符合函數(shù)式編程的代碼:
1defprintArgs ( args:Array[String]):Unit={
2for( arg <- args)
3println(arg)
4}
或者更簡化為:
1defprintArgs ( args:Array[String]):Unit={
2args.foreach(println)
3}
這個例子也說明了盡量少用vars的好處,代碼更簡潔和明了顿苇,從而也可以減少錯誤的發(fā)生峭咒。因此Scala編程的一個基本原則上,能不用Vars纪岁,盡量不用vars,能不用mutable變量讹语,盡量不用mutable變量,能避免函數(shù)的副作用蜂科,盡量不產(chǎn)生副作用顽决。
第十二步: 讀取文件
使用腳本實(shí)現(xiàn)某個任務(wù),通常需要讀取文件导匣,本節(jié)介紹Scala讀寫文件的基本方法才菠。比如下面的例子讀取文件的每行,把該行字符長度添加到行首:
1importscala.io.Source
2if(args.length >0){
3for( line <- Source.fromFile(args(0)).getLines())
4println(line.length +" "+ line)
5}
6else
7Console.err.println("Please enter filename")
可以看到Scala引入包的方式和Java類似贡定,也是通過import語句赋访。文件相關(guān)的類定義在scala.io包中。 如果需要引入多個類缓待,Scala使用 “_” 而非 “*”.
通過前面兩篇文章的介紹蚓耽,你應(yīng)該對Scala編程有了一個大概的了解,可以編寫一些簡單的Scala腳本語言旋炒。Scala的功能遠(yuǎn)遠(yuǎn)不止這些步悠,在后面的文章我們在一一詳細(xì)介紹。