Scala 指南
Scala 指南
開始精彩的Scala旅程
?下一頁(yè)Hello World
Scala 是一門 函數(shù)式的面向?qū)ο?/strong>語(yǔ)言。他運(yùn)行在Java虛擬機(jī)上.
本指南是用來介紹Scala強(qiáng)大的功能。同時(shí)你可以親身實(shí)踐他們。
點(diǎn)擊左邊的Run按鈕試試。左邊的運(yùn)行框可以在遠(yuǎn)程編譯和運(yùn)行任何Scala代碼。運(yùn)行的結(jié)果將展示在下面對(duì)文本框中。你也可以試著編輯這些代碼,重新運(yùn)行。(初次運(yùn)行可能會(huì)有些慢,不過第二次就很快了)
使用方向鍵或者空格來翻頁(yè)
?下一頁(yè)基礎(chǔ)開始
目錄(基礎(chǔ))
- 表達(dá)式和值
- 函數(shù)是一等公民
- 借貸模式
- 按名稱傳遞參數(shù)
- 定義類
- 鴨子類型
- 柯里化
- 范型
- Traits
表達(dá)式和值
在Scala中,幾乎所有的語(yǔ)言元素都是表達(dá)式。
println("hello wolrd")是一個(gè)表達(dá)式,
"hello"+" world"也是一個(gè)表達(dá)式。
可以通過val定義一個(gè)常量,亦可以通過var定義一個(gè)變量。推薦多使用常量。
?目錄 ?下一頁(yè)函數(shù)是一等公民
可以使用def來定義一個(gè)函數(shù)。函數(shù)體是一個(gè)表達(dá)式。
使用Block表達(dá)式的時(shí)候,默認(rèn)最后一行的返回是返回值,無(wú)需顯式指定。
函數(shù)還可以像值一樣,賦值給var或val。因此函數(shù)也可以作為參數(shù)傳給另一個(gè)函數(shù)。
?上一頁(yè) ?目錄 ?下一頁(yè)借貸模式
由于函數(shù)可以像值一樣作為參數(shù)傳遞,所以可以方便的實(shí)現(xiàn)借貸模式。
這個(gè)例子是從/proc/self/stat文件中讀取當(dāng)前進(jìn)程的pid。
withScanner封裝了try-finally塊,所以調(diào)用者不用再close。
注:當(dāng)表達(dá)式?jīng)]有返回值時(shí),默認(rèn)返回Unit。
?上一頁(yè) ?目錄 ?下一頁(yè)按名稱傳遞參數(shù)
這個(gè)例子演示了按名稱傳遞參數(shù),由于有除以0,所以運(yùn)行該程序會(huì)產(chǎn)生異常。
試著將
def log(msg: String)修改為
def log(msg: => String)由按值傳遞修改為按名稱傳遞后將不會(huì)產(chǎn)生異常。
因?yàn)閘og函數(shù)的參數(shù)是按名稱傳遞,參數(shù)會(huì)等到實(shí)際使用的時(shí)候才會(huì)計(jì)算,所以被跳過。
按名稱傳遞參數(shù)可以減少不必要的計(jì)算和異常。
?上一頁(yè) ?目錄 ?下一頁(yè)定義類
可以用class關(guān)鍵字來定義類。并通過new來創(chuàng)建類。
在定義類時(shí)可以定義字段,如firstName,lastName。這樣做還可以自動(dòng)生成構(gòu)造函數(shù)。
可以在類中通過def定義函數(shù)。var和val定義字段。
函數(shù)名是任何字符如+,-,*,/。
試著將
obama.age_=(51)簡(jiǎn)化為
obama.age = 51這樣的簡(jiǎn)化更像調(diào)用一個(gè)變量。
?上一頁(yè) ?目錄 ?下一頁(yè)鴨子類型
走起來像鴨子,叫起來像鴨子,就是鴨子。
這個(gè)例子中使用
{ def close(): Unit }作為參數(shù)類型。因此任何含有close()的函數(shù)的類都可以作為參數(shù)。
不必使用繼承這種不夠靈活的特性。
?上一頁(yè) ?目錄 ?下一頁(yè)柯里化
這個(gè)例子和上面的功能相同。不同的是使用了柯里化(Currying)技術(shù)。
def add(x:Int, y:Int) = x + y是普通的函數(shù)
def add(x:Int) = (y:Int) => x + y是柯里化后的函數(shù),相當(dāng)于返回一個(gè)匿名函數(shù)表達(dá)式。
def add(x:Int)(y:Int) = x + y是簡(jiǎn)化寫法
柯里化可以讓我們構(gòu)造出更像原生語(yǔ)言提供的功能的代碼
試著將例子中的withclose(...)(...)換成withclose(...){...}
?上一頁(yè) ?目錄 ?下一頁(yè)范型
之前的例子可以使用泛型變得更簡(jiǎn)潔更靈活。
試著將
"123456"修改為
123456雖然msg由String類型變?yōu)镮nt類型,但是由于使用了泛型,代碼依舊可以正常運(yùn)行。
?上一頁(yè) ?目錄 ?下一頁(yè)Traits
Traits就像是有函數(shù)體的Interface。使用with關(guān)鍵字來混入。
這個(gè)例子是給java.util.ArrayList添加了foreach的功能。
試著再在with ForEachAble[Int]后面加上
with JsonAble給list添加toJson的能力
?上一頁(yè) ?目錄?下一頁(yè)函數(shù)式開始
目錄(函數(shù)式)
- 模式匹配
- Case Class
- 函數(shù)式的威力
- 函數(shù)式真正的威力
- Word Count
- 尾遞歸
- 更強(qiáng)大的For循環(huán)
- Option
- Lazy初始化
模式匹配
模式匹配是類似switch-case特性,但更加靈活;也類似if-else,但更加簡(jiǎn)約。
這個(gè)例子展示的使用用模式匹配實(shí)現(xiàn)斐波那契。 使用case來匹配參數(shù),如果case _,則可以匹配任何參數(shù)。
試著將
case n: Int修改為
case n: Int if (n > 1)case后添加if語(yǔ)句判斷,這樣修改當(dāng)輸入負(fù)數(shù)時(shí),就不會(huì)無(wú)限循環(huán)。
模式匹配也可以匹配類型,在case _ 前加上
case n: String => fibonacci(n.toInt)這樣就可以匹配String類型
?目錄 ?下一頁(yè)Case Class
case class 顧名思義就是為case語(yǔ)句專門設(shè)計(jì)的類, 在普通類的基礎(chǔ)上添加了和類名一直的工廠方法, 還添加了hashcode,equals和toString等方法。
由于使用了require(n >= 0)來檢驗(yàn)參數(shù),如果使用負(fù)數(shù)計(jì)算,將會(huì)拋出異常。
?上一頁(yè) ?目錄 ?下一頁(yè)函數(shù)式的威力
這個(gè)例子是用指令式編程判斷一個(gè)List中是否含有奇數(shù)。
試著將
containsOdd(list)替換為
list.exists((x: Int) => x % 2 ==1)通過將函數(shù)作為參數(shù),可以使程序更簡(jiǎn)潔。還可以再簡(jiǎn)化為
list.exists(_ % 2 == 1)可以用_替代參數(shù)
相比于Ruby等動(dòng)態(tài)語(yǔ)言,這威力來自于科學(xué)而不是魔法
?上一頁(yè) ?目錄 ?下一頁(yè)函數(shù)式真正的威力
函數(shù)式除了能簡(jiǎn)化代碼外,更重要的是他關(guān)注的是Input和Output,函數(shù)本身沒有副作用。
就是Unix pipeline一樣,簡(jiǎn)單的命令組合在一起威力無(wú)窮。
如果你喜歡Unix pipeline的方式,你一定也會(huì)喜歡函數(shù)式編程。
這個(gè)例子是用函數(shù)式的代碼模擬
cat file | grep 'warn' | grep '2013' | wcList的filter方法接受一個(gè)過濾函數(shù),返回一個(gè)新的List,作為下一個(gè)方法的輸入。
?上一頁(yè) ?目錄 ?下一頁(yè)Word Count
Word Count是一個(gè)MapReduce的一個(gè)經(jīng)典示例。在函數(shù)式編程中,Word Count最直觀的實(shí)現(xiàn)方法也是MapReduce。
這個(gè)例子介紹了List的兩個(gè)重要的高階方法map和reduceLeft。
map接受一個(gè)轉(zhuǎn)換函數(shù),返回轉(zhuǎn)換結(jié)果。
reduceLeft接受一個(gè)合并函數(shù),依次遍歷合并。
使用高階方法可以代替大部分需要循環(huán)的操作,使代碼更清晰
?上一頁(yè) ?目錄 ?下一頁(yè)尾遞歸
尾遞歸是遞歸的一種,特點(diǎn)在于會(huì)在函數(shù)的最末調(diào)用自身。尾遞歸是函數(shù)式編程的常見寫法。
這個(gè)例子是foldLeft的尾遞歸實(shí)現(xiàn)。foldLeft和reduceLeft相比更常用,多一個(gè)初始參數(shù)。
當(dāng)用List做match case時(shí)。可以使用 :: 來解構(gòu)。返回第一個(gè)元素head和剩余元素tail。
注:尾遞歸會(huì)在編譯期優(yōu)化,因此不用擔(dān)心遞歸造成的棧溢出問題。
?上一頁(yè) ?目錄 ?下一頁(yè)更強(qiáng)大的For循環(huán)
循環(huán)語(yǔ)句是指令式編程的常見語(yǔ)句,Scala對(duì)其加以改進(jìn),成為適應(yīng)函數(shù)式風(fēng)格的利器。
For循環(huán)也是有返回值的,返回的是一個(gè)List。在每一輪迭代中加入yield,yield后的值可以加入到List中。
這個(gè)例子是使用for循環(huán)代替map函數(shù)。
?上一頁(yè) ?目錄 ?下一頁(yè)Option
Scala提供了Option機(jī)制來解決,代碼中不斷檢查null的問題。
這個(gè)例子包裝了getProperty方法,使其返回一個(gè)Option。 這樣就可以不再漫無(wú)目的地null檢查。只要Option類型的值即可。
使用pattern match來檢查是常見做法。也可以使用getOrElse來提供當(dāng)為None時(shí)的默認(rèn)值。
給力的是Option還可以看作是最大長(zhǎng)度為1的List,List的強(qiáng)大功能都可以使用。
再見 NullException
?上一頁(yè) ?目錄 ?下一頁(yè)Lazy初始化
Lazy可以延遲初始化字段。加上lazy的字段會(huì)在第一次訪問的時(shí)候初始化,而不是類初始化的時(shí)候初始化。
這個(gè)例子是從github獲得Scala的版本號(hào),由于訪問網(wǎng)絡(luò)需要較多時(shí)間。可以使用lazy來延遲獲取。 防止可能的浪費(fèi)。
Lazy非常適合于初始化非常耗時(shí)的場(chǎng)景
?上一頁(yè) ?目錄?下一頁(yè)并發(fā)開始
目錄(并發(fā))
- 使用Actor
- Actor更簡(jiǎn)化的用法
- Actor原理
- 同步返回
- 異步返回
- 并行集合
- 并行wordcount
- 遠(yuǎn)程Actor
使用Actor
Actor是Scala的并發(fā)模型。在2.10之后的版本中,使用Akka作為其推薦Actor實(shí)現(xiàn)。
Actor是類似線程的實(shí)體,有一個(gè)郵箱。 Actor可以通過system.actorOf來創(chuàng)建,receive獲取郵箱消息,!向郵箱發(fā)送消息。
這個(gè)例子是一個(gè)EchoServer,接受信息并打印。
?目錄 ?下一頁(yè)Actor更簡(jiǎn)化的用法
可以通過更簡(jiǎn)化的辦法聲明Actor。
通過
akka.actor.ActorDSL中的actor函數(shù)。這個(gè)函數(shù)可以接受一個(gè)Actor的構(gòu)造器Act,啟動(dòng)并返回Actor。
?上一頁(yè) ?目錄 ?下一頁(yè)Actor原理
Actor比線程輕量。在Scala中可以創(chuàng)建數(shù)以百萬(wàn)級(jí)的Actor。奧秘在于Actor直接可以復(fù)用線程。
Actor和線程是不同的抽象,他們的對(duì)應(yīng)關(guān)系是由Dispatcher決定的。
這個(gè)例子創(chuàng)建4個(gè)Actor,每次調(diào)用的時(shí)候打印自身線程名稱。
可以發(fā)現(xiàn)Actor和線程之間沒有一對(duì)一的對(duì)應(yīng)關(guān)系。一個(gè)Actor可以使用多個(gè)線程,一個(gè)線程也會(huì)被多個(gè)Actor復(fù)用。
?上一頁(yè) ?目錄 ?下一頁(yè)同步返回
Actor非常適合于較耗時(shí)的操作。比如獲取網(wǎng)絡(luò)資源。
這個(gè)例子通過調(diào)用ask函數(shù)來獲取一個(gè)Future。
在Actor內(nèi)部通過 sender ! 傳遞結(jié)果。
Future像Option一樣有很多高階方法,可以使用foreach查看結(jié)果。
?上一頁(yè) ?目錄 ?下一頁(yè)異步返回
異步操作可以最大發(fā)揮效能。Scala的Futrue很強(qiáng)大,可以異步返回。
可以實(shí)現(xiàn)Futrue的onComplete方法。當(dāng)Futrue結(jié)束的時(shí)候就會(huì)回調(diào)。
在調(diào)用ask的時(shí)候,可以設(shè)定超時(shí)。
?上一頁(yè) ?目錄 ?下一頁(yè)并行集合
這個(gè)例子是訪問若干URL,并記錄時(shí)間。如果能并行訪問,就可以大幅提高性能。
嘗試將
urls.map修改為
urls.par.map這樣每個(gè)map中的函數(shù)都可以并發(fā)執(zhí)行。
當(dāng)函數(shù)式和并發(fā)結(jié)合,就會(huì)這樣讓人興奮。
?上一頁(yè) ?目錄 ?下一頁(yè)并行wordcount
這個(gè)例子是訪問若干URL,并記錄時(shí)間。
并行集合支持大部分集合的功能。
在前面有一個(gè)wordcount例子,也可以用并行集合加以實(shí)現(xiàn)。
不增加程序復(fù)雜性,卻能大幅提高利用多核的能力。
?上一頁(yè) ?目錄 ?下一頁(yè)遠(yuǎn)程Actor
Actor是并發(fā)模型,也使用于分布式。
這個(gè)例子創(chuàng)建一個(gè)Echo服務(wù)器,通過actorOf來注冊(cè)自己。
然后再創(chuàng)建一個(gè)client,通過akka url來尋址。
除了是通過url創(chuàng)建的,其他使用的方法和普通Actor一樣。
?上一頁(yè) ?目錄?下一頁(yè)實(shí)踐開始
目錄(實(shí)踐)
- 使用Java
- 相等性
- 抽取器
- 記憶模式
- 隱式轉(zhuǎn)換
- DSL
- 測(cè)試
- Simple Build Tool
使用Java
Scala和Java可以非常方便的互操作,前面已經(jīng)有大量Scala直接使用Java的例子。
同樣Java也可以使用Scala。這個(gè)例子演示使用@BeanProperty注解來生成Java Style的Bean。
嘗試將
var name: String修改為
@BeanProperty var name: String這樣就給bean添加了getter/setter。 Apache BeanUtils就可以正常工作。
?目錄 ?下一頁(yè)相等性
在Scala中==等效于equals,這一點(diǎn)和Java不同。更自然一些。
寫一個(gè)完全正確的equal函數(shù)并不容易,這個(gè)例子也有子類會(huì)不對(duì)稱的Bug。
嘗試將class修改為case class,并刪除equals函數(shù)。
case類會(huì)自動(dòng)生成正確的equals函數(shù)。
?上一頁(yè) ?目錄 ?下一頁(yè)抽取器
抽取器可以幫助模式匹配進(jìn)行解構(gòu)。
這個(gè)例子是構(gòu)建一個(gè)Email抽取器,只要實(shí)現(xiàn)unapply函數(shù)就可以了。
Scala的正則表達(dá)式會(huì)自帶抽取器,可以抽取出一個(gè)List。List里的元素是匹配()里的表達(dá)式。
抽取器很有用,短短的例子里就有兩處使用抽取器:
case user :: domain :: Nil解構(gòu)List
case Email(user, domain)解構(gòu)Email。
?上一頁(yè) ?目錄 ?下一頁(yè)記憶模式
記憶模式可以解決手動(dòng)編寫存取cache代碼的麻煩。
這個(gè)例子中,memo可以將一個(gè)不含cache函數(shù),包裝成一個(gè)含有cache功能的。
還是斐波那契的例子,通過cache可以使性能提高。
嘗試將
fibonacci_(n - 1) + fibonacci_(n - 2)修改為
memo(fibonacci_)(n - 1) + memo(fibonacci_)(n - 2)可以提高更多性能。
?上一頁(yè) ?目錄 ?下一頁(yè)隱式轉(zhuǎn)換
implicit可以定義一個(gè)轉(zhuǎn)換函數(shù),可以在使用相應(yīng)類型的時(shí)候自動(dòng)轉(zhuǎn)換。
這個(gè)例子可以將String自動(dòng)轉(zhuǎn)換為Date類型。隱式轉(zhuǎn)換時(shí)實(shí)現(xiàn)DSL的重要工具。
?上一頁(yè) ?目錄 ?下一頁(yè)DSL
DSL是Scala最強(qiáng)大武器,可以使一些描述性代碼變得極為簡(jiǎn)單。
這個(gè)例子是使用DSL生成JSON。Scala很多看似是語(yǔ)言級(jí)的特性也是用DSL做到的。
自己編寫DSL有點(diǎn)復(fù)雜,但使用起來非常方便。
?上一頁(yè) ?目錄 ?下一頁(yè)測(cè)試
Scala可以使用Spec2,ScalaTest來測(cè)試, DSL可以使測(cè)試更方便。
這個(gè)例子是測(cè)試一個(gè)階乘函數(shù)。使用should/in來建立測(cè)試用例。
測(cè)試是默認(rèn)并發(fā)執(zhí)行的。
?上一頁(yè) ?目錄 ?下一頁(yè)Simple Build Tool
SBT是Scala的最佳編譯工具,在他的幫助下,
你甚至不需要安裝除JRE外的任何東西,來開發(fā)Scala。
例如你想在自己的機(jī)器上執(zhí)行這個(gè)Scala-Tour,可以執(zhí)行左邊的命令
?上一頁(yè) ?目錄?下一頁(yè)關(guān)于
這個(gè)指南源自于作者對(duì)Scala的熱愛和對(duì)傳播Scala的愿望
其他資料:
- Scala School!: Twitter的Scala教學(xué)
- A Tour of Scala:官方Scala Tour
- Scala By Example:更多的Scala例程
- Scala Cheatsheet:一眼望穿Scala基本語(yǔ)法
- Functional Programming Principles in Scala:Coursera 的Scala課程
from: http://zh.scala-tour.com/#/basics-contents
總結(jié)
- 上一篇: Sed教程(五):管理模式、正则表达式、
- 下一篇: 快速了解Scala技术栈