鍍金池/ 教程/ Scala/ 進(jìn)一步 Scala
包對(duì)象
Ordered Trait
組合和繼承–定義 final 成員
基本數(shù)據(jù)類(lèi)型
Match 表達(dá)式
類(lèi)和對(duì)象 (三)
操作基本數(shù)據(jù)類(lèi)型
for 表達(dá)式
組合和繼承–重載成員函數(shù)和方法
類(lèi)和對(duì)象 (二)
組合和繼承–定義 factory 對(duì)象
組合和繼承–多態(tài)和動(dòng)態(tài)綁定
Trait 的基本概念
if 表達(dá)式
組合和繼承–抽象類(lèi)
函數(shù)–函數(shù)字面量的一些簡(jiǎn)化寫(xiě)法
while 循環(huán)
組合和繼承–使用組合還是繼承?
訪問(wèn)控制修飾符
Trait 示例–Rectangular 對(duì)象
組合和繼承–定義參數(shù)化成員變量
組合和繼承–定義無(wú)參數(shù)方法
類(lèi)和對(duì)象 (一)
函數(shù)–閉包
函數(shù)–類(lèi)成員函數(shù)
Scala 基本數(shù)據(jù)類(lèi)型的實(shí)現(xiàn)方法
try 表達(dá)式處理異常
選擇瘦接口還是胖接口設(shè)計(jì)?
組合和繼承–小結(jié)
創(chuàng)建新的控制結(jié)構(gòu)
使用 import
為訪問(wèn)控制修飾符添加作用域
Scala 的類(lèi)層次關(guān)系
類(lèi)和對(duì)象 (五)
傳名參數(shù)
柯里化函數(shù)
函數(shù)–頭等公民
組合和組合和繼承–定義 heighten 和 widen 函數(shù)
使用 Package–將代碼放入包中
隱含的 import
所有類(lèi)的公共子類(lèi)–底層類(lèi)型
進(jìn)一步 Scala
函數(shù)–局部函數(shù)
引用包中的代碼
組合和繼承–使用 override 修飾符
組合和繼承–實(shí)現(xiàn)類(lèi) Element 的 above,beside 和 toString()方法
類(lèi)和對(duì)象 (四)
函數(shù)–尾遞歸
沒(méi)有“break”和“continue”的日子
組合和繼承–調(diào)用基類(lèi)構(gòu)造函數(shù)
減低代碼重復(fù)
函數(shù)–函數(shù)–可變參數(shù),命名參數(shù),缺省參數(shù)
起步 Scala
組合和繼承–擴(kuò)展類(lèi)
函數(shù)–部分應(yīng)用的函數(shù)
開(kāi)始神奇的 Scala編程之旅
組合和繼承–概述
Trait 用來(lái)實(shí)現(xiàn)可疊加的修改操作

進(jìn)一步 Scala

本篇繼續(xù)上一篇對(duì) Scala 的整體介紹,本篇進(jìn)一步解釋 Scala 的一些高級(jí)特性,當(dāng)你學(xué)完本篇后,就有足夠的知識(shí)編寫(xiě)一些實(shí)用的 Scala 腳本應(yīng)用了。

第七步:使用類(lèi)型參數(shù)化數(shù)組

在 Scala 中你可以使用 new 來(lái)實(shí)例化一個(gè)類(lèi)。當(dāng)你創(chuàng)建一個(gè)對(duì)象的實(shí)例時(shí),你可以使用數(shù)值或類(lèi)型參數(shù)。如果使用類(lèi)型參數(shù),它的作用類(lèi)似 Java 或 .Net 的 Generic 類(lèi)型。所不同的是 Scala 使用方括號(hào)來(lái)指明數(shù)據(jù)類(lèi)型參數(shù),而非尖括號(hào)。比如

val greetStrings =new Array[String](3)
greetStrings(0)="Hello"
greetStrings(1)=","
greetStrings(2)="world!\n"
for(i <- 0 to 2)
  print(greetStrings(i))

可以看到 Scala 使用[]來(lái)為數(shù)組指明類(lèi)型化參數(shù),本例使用 String 類(lèi)型,數(shù)組使用()而非[]來(lái)指明數(shù)組的索引。其中的 for 表達(dá)式中使用到 0 to 2 ,這個(gè)表達(dá)式演示了 Scala 的一個(gè)基本規(guī)則,如果一個(gè)方法只有一個(gè)參數(shù),你可以不用括號(hào)和. 來(lái)調(diào)用這個(gè)方法。因此這里的 0 to 2, 其實(shí)為(0).to(2) 調(diào)用的為整數(shù)類(lèi)型的 to 方法,to 方法使用一個(gè)參數(shù)。

Scala 中所有的基本數(shù)據(jù)類(lèi)型也是對(duì)象(和 Java 不同),因此 0 可以有方法(實(shí)際上調(diào)用的是 RichInt 的方法),這種只有一個(gè)參數(shù)的方法可以使用操作符的寫(xiě)法(不用.和括號(hào)),實(shí)際上 Scala 中表達(dá)式 1+2,最終解釋為 (1).+(2) + 也是 Int 的一個(gè)方法,和 Java 不同的是,Scala 對(duì)方法的名稱(chēng)沒(méi)有太多的限制,你可以使用符合作為方法的名稱(chēng)。

這里也說(shuō)明為什么 Scala 中使用()來(lái)訪問(wèn)數(shù)組元素,在 Scala 中,數(shù)組和其它普遍的類(lèi)定義一樣,沒(méi)有什么特別之處,當(dāng)你在某個(gè)值后面使用()時(shí),Scala 將其翻譯成對(duì)應(yīng)對(duì)象的 apply 方法。因此本例中 greetStrings(1) 其實(shí)調(diào)用 greetString.apply(1) 方法。這種表達(dá)方法不僅僅只限于數(shù)組,對(duì)于任何對(duì)象,如果在其后面使用(),都將調(diào)用該對(duì)象的 apply 方法。同樣的如果對(duì)某個(gè)使用()的對(duì)象賦值,比如:

greetStrings(0)="Hello"

Scala 將這種賦值轉(zhuǎn)換為該對(duì)象的 update 方法, 也就是 greetStrings.update(0,”hello”)。因此上面的例子,使用傳統(tǒng)的方法調(diào)用可以寫(xiě)成:

val greetStrings =new Array[String](3)
greetStrings.update(0,"Hello")
greetStrings.update(1,",")
greetStrings.update(2,"world!\n")
for(i <- 0 to 2)
  print(greetStrings.apply(i))

從這點(diǎn)來(lái)說(shuō),數(shù)組在 Scala 中并不某種特殊的數(shù)據(jù)類(lèi)型,和普通的類(lèi)沒(méi)有什么不同。

不過(guò) Scala 還是提供了初始化數(shù)組的簡(jiǎn)單的方法,比如什么的例子數(shù)組可以使用如下代碼:

val greetStrings =Array("Hello",",","World\n")

這里使用()其實(shí)還是調(diào)用 Array 類(lèi)的關(guān)聯(lián)對(duì)象 Array 的 apply 方法,也就是

val greetStrings =Array.apply("Hello",",","World\n")

第八步: 使用Lists

Scala 也是一個(gè)面向函數(shù)的編程語(yǔ)言,面向函數(shù)的編程語(yǔ)言的一個(gè)特點(diǎn)是,調(diào)用某個(gè)方法不應(yīng)該有任何副作用,參數(shù)一定,調(diào)用該方法后,返回一定的結(jié)果,而不會(huì)去修改程序的其它狀態(tài)(副作用)。這樣做的一個(gè)好處是方法和方法之間關(guān)聯(lián)性較小,從而方法變得更可靠和重用性高。使用這個(gè)原則也就意味著就變量的設(shè)成不可修改的,這也就避免了多線程訪問(wèn)的互鎖問(wèn)題。

前面介紹的數(shù)組,它的元素是可以被修改的。如果需要使用不可以修改的序列,Scala 中提供了 Lists 類(lèi)。和 Java 的 List 不同,Scala 的 Lists 對(duì)象是不可修改的。它被設(shè)計(jì)用來(lái)滿足函數(shù)編程風(fēng)格的代碼。它有點(diǎn)像 Java 的 String,String 也是不可以修改的,如果需要可以修改的 String 對(duì)像,可以使用 StringBuilder 類(lèi)。

比如下面的代碼:

val oneTwo = List(1,2)
val threeFour = List(3,4)
val oneTwoThreeFour=oneTwo ::: threeFour
println (oneTwo + " and " + threeFour + " were not mutated.")
println ("Thus, " + oneTwoThreeFour + " is a new list")

定義了兩個(gè) List 對(duì)象 oneTwo 和 threeFour,然后通過(guò):::操作符(其實(shí)為:::方法)將兩個(gè)列表鏈接起來(lái)。實(shí)際上由于 List 的不可以修改特性,Scala 創(chuàng)建了一個(gè)新的 List 對(duì)象 oneTwoThreeFour 來(lái)保存兩個(gè)列表連接后的值。

List 也提供了一個(gè)::方法用來(lái)向 List 中添加一個(gè)元素,::方法(操作符)是右操作符,也就是使用::右邊的對(duì)象來(lái)調(diào)用它的::方法,Scala 中規(guī)定所有以:開(kāi)頭的操作符都是右操作符,因此如果你自己定義以:開(kāi)頭的方法(操作符)也是右操作符。

如下面使用常量創(chuàng)建一個(gè)列表:

val oneTowThree = 1 :: 2 ::3 :: Nil
println(oneTowThree)

調(diào)用空列表對(duì)象 Nil 的 ::方法 也就是

val oneTowThree =  Nil.::(3).::(2).::(1)

Scala 的 List 類(lèi)還定義其它很多很有用的方法,比如 head, last,length, reverse,tail 等這里就不一一說(shuō)明了,具體可以參考 List 的文檔。

第九步:使用元組( Tuples )

Scala 中另外一個(gè)很有用的容器類(lèi)為 Tuples,和 List 不同的 Tuples 可以包含不同類(lèi)型的數(shù)據(jù),而 List 只能包含同類(lèi)型的數(shù)據(jù)。Tuples 在方法需要返回多個(gè)結(jié)果時(shí)非常有用。( Tuple 對(duì)應(yīng)到數(shù)學(xué)的矢量的概念)。

一旦定義了一個(gè)元組,可以使用 ._和索引來(lái)訪問(wèn)員組的元素(矢量的分量,注意和數(shù)組不同的是,元組的索引從 1 開(kāi)始)。

val pair=(99,"Luftballons")
println(pair._1)
println(pair._2)

元祖的實(shí)際類(lèi)型取決于它的分量的類(lèi)型,比如上面 pair 的類(lèi)型實(shí)際為 Tuple2[Int,String],而 (‘u’,’r’,”the”,1,4,”me”) 的類(lèi)型為 Tuple6[Char,Char,String,Int,Int,String]。

目前 Scala 支持的元祖的最大長(zhǎng)度為 22。如果有需要,你可以自己擴(kuò)展更長(zhǎng)的元祖。

第十步: 使用 Sets 和 Maps

Scala 語(yǔ)言的一個(gè)設(shè)計(jì)目標(biāo)是讓程序員可以同時(shí)利用面向?qū)ο蠛兔嫦蚝瘮?shù)的方法編寫(xiě)代碼,因此它提供的集合類(lèi)分成了可以修改的集合類(lèi)和不可以修改的集合類(lèi)兩大類(lèi)型。比如 Array 總是可以修改內(nèi)容的,而 List 總是不可以修改內(nèi)容的。類(lèi)似的情況,Scala 也提供了兩種 Sets 和 Map 集合類(lèi)。

比如 Scala API 定義了 Set 的基 Trait 類(lèi)型 Set( Trait 的概念類(lèi)似于 Java 中的 Interface,所不同的 Scala 中的 Trait 可以有方法的實(shí)現(xiàn)),分兩個(gè)包定義 Mutable (可變)和 Immutable (不可變),使用同樣名稱(chēng)的子 Trait。下圖為 Trait 和類(lèi)的基礎(chǔ)關(guān)系:

http://wiki.jikexueyuan.com/project/scala-development-guide/images/9.png" alt="" />

使用 Set 的基本方法如下:

var jetSet = Set ("Boeing","Airbus")
jetSet +="Lear"
println(jetSet.contains("Cessna"))

缺省情況 Set 為 Immutable Set,如果你需要使用可修改的集合類(lèi)( Set 類(lèi)型),你可以使用全路徑來(lái)指明 Set,比如 scala.collection.mutalbe.Set 。

Scala 提供的另外一個(gè)類(lèi)型為 Map 類(lèi)型,Scala 也提供了 Mutable 和 Immutable 兩種 Map 類(lèi)型。

http://wiki.jikexueyuan.com/project/scala-development-guide/images/10.png" alt="" />

Map 的基本用法如下( Map 類(lèi)似于其它語(yǔ)言中的關(guān)聯(lián)數(shù)組如 PHP )

val romanNumeral = Map ( 1 -> "I" , 2 -> "II",
  3 -> "III", 4 -> "IV", 5 -> "V")
println (romanNumeral(4))

第十一步: 學(xué)習(xí)識(shí)別函數(shù)編程風(fēng)格

Scala 語(yǔ)言的一個(gè)特點(diǎn)是支持面向函數(shù)編程,因此學(xué)習(xí) Scala 的一個(gè)重要方面是改變之前的指令式編程思想(尤其是來(lái)自 Java 或 C# 背景的程序員),觀念要想函數(shù)式編程轉(zhuǎn)變。首先在看代碼上要認(rèn)識(shí)哪種是指令編程,哪種是函數(shù)式編程。實(shí)現(xiàn)這種思想上的轉(zhuǎn)變,不僅僅會(huì)使你成為一個(gè)更好的 Scala 程序員,同時(shí)也會(huì)擴(kuò)展你的視野,使你成為一個(gè)更好的程序員。

一個(gè)簡(jiǎn)單的原則,如果代碼中含有 var 類(lèi)型的變量,這段代碼就是傳統(tǒng)的指令式編程,如果代碼只有 val 變量,這段代碼就很有可能是函數(shù)式代碼,因此學(xué)會(huì)函數(shù)式編程關(guān)鍵是不使用 vars 來(lái)編寫(xiě)代碼。

來(lái)看一個(gè)簡(jiǎn)單的例子:

def printArgs ( args: Array[String]) : Unit ={
    var i=0
    while (i < args.length) {
      println (args(i))
      i+=1
    }
}

來(lái)自 Java 背景的程序員開(kāi)始寫(xiě) Scala 代碼很有可能寫(xiě)成上面的實(shí)現(xiàn)。我們?cè)囍コ?vars 變量,可以寫(xiě)成跟符合函數(shù)式編程的代碼:

def printArgs ( args: Array[String]) : Unit ={
    for( arg <- args)
      println(arg)
}

或者更簡(jiǎn)化為:

def printArgs ( args: Array[String]) : Unit ={
    args.foreach(println)
}

這個(gè)例子也說(shuō)明了盡量少用 vars 的好處,代碼更簡(jiǎn)潔和明了,從而也可以減少錯(cuò)誤的發(fā)生。因此 Scala 編程的一個(gè)基本原則上,能不用 Vars,盡量不用 vars,能不用 mutable變量,盡量不用 mutable變量,能避免函數(shù)的副作用,盡量不產(chǎn)生副作用。

第十二步: 讀取文件

使用腳本實(shí)現(xiàn)某個(gè)任務(wù),通常需要讀取文件,本節(jié)介紹 Scala 讀寫(xiě)文件的基本方法。比如下面的例子讀取文件的每行,把該行字符長(zhǎng)度添加到行首:

import scala.io.Source
if (args.length >0 ){
  for( line <- Source.fromFile(args(0)).getLines())
    println(line.length + " " + line)
}
   else
      Console.err.println("Please enter filename")

可以看到 Scala 引入包的方式和 Java 類(lèi)似,也是通過(guò) import 語(yǔ)句。文件相關(guān)的類(lèi)定義在 scala.io 包中。 如果需要引入多個(gè)類(lèi),Scala 使用 “_” 而非 “*”。

通過(guò)前面兩篇文章的介紹,你應(yīng)該對(duì) Scala 編程有了一個(gè)大概的了解,可以編寫(xiě)一些簡(jiǎn)單的 Scala 腳本語(yǔ)言。Scala 的功能遠(yuǎn)遠(yuǎn)不止這些,在后面的文章我們?cè)谝灰辉敿?xì)介紹。