Scala Handbook(轉載)

Scala 2.8+ Handbook php

 

Scala的向後兼容性沒有Java那麼謹慎,2.8+相對於2.7有很大變化,本文檔針對2.8+ html

 

 和別人分享你的知識,那纔是永恆之道」——somebody java

 

前言: node

這只是份簡單的筆記,本不該敝履自珍,但手癢難耐仍是寫點廢話在前面。 react

書籍浩如煙海,技術方面的書也汗牛充棟,惋惜咱們的閱讀速度和理解力、記憶力太有限,每每費力學懂的知識轉眼就變得很是陌生,「博聞強志、過目不忘」者畢竟罕見。對於大部分人來講,即使昔日信手拈來的東西,時間久了也會毫無頭緒。因此知識不在於曾經學過多少,而在於你記住並還能運用多少。 git

「好記性不如爛筆頭」——近年來我慢慢習慣把費力學習的東西都作一個筆記,一是在學習的過程當中加深印象,畢竟技術學習不一樣於欣賞娛樂大片和瀏覽娛樂新聞看個過眼煙雲;二是便於學而「時習之」,書上的東西通常是針對不一樣技術背景的讀者,有不少做者費力用墨之處對你來講純屬廢話,而他一筆帶過的地方偏偏讓你困惑不已。一本讀書筆記至關於你對書的「註解」。 程序員

Scala很好玩頗有趣,但絕對不是一門簡單易懂的編程語言(Java1.07.0,一直作++;而Scala不光作++,也作--)。對於從Java或者其餘FP走過來的人,Scala有不少「彆扭」的用法,很難記清楚用正確。學Scala,最佳的作法是把它用到平常的應用開發中,不斷加深記憶。但即使你準備這麼作了,手頭沒有一份方便的備查材料,剛開始也會步履艱難。我在使用的過程當中也有這個體會,因此纔不怨其煩地把一些學來並嘗試過的東西記在本文檔中備查,以便以後能行雲流水地「玩轉」它。整個寫筆記的過程,沒有孤寂,而是沉浸在學習新知的興奮和快意中,但願這種快樂也能放大傳遞給更多的人。 github

我的認爲,對於一門編程語言使用中的查閱,大體有幾個階段:查教程(tutorial)——》查手冊(handbook)——》查本身寫的庫。這個材料,不是嚴格的教程,或手冊,而是介於這二者間。Scala目前已經出版了幾本書,這些書從各自的角度解讀Scala,好比Scala做者的「Programming Scala」,應該是最權威的Scala書。但好書不必定好讀,一是太厚,二是無趣,不如自制的來得貼心。正如數學問題要用公式表達最清楚,編程問題得用圖表和代碼表示才最清楚,這兩者也是本文中使用最多的表達方式,我儘可能採用簡短的代碼來講明問題(簡短代碼也能說明不少事情,廣受讚譽的Effective Java基本沒有超過一頁的程序代碼)。可以熟練使用Java的程序員,參考本筆記,應該能夠自如地開始着手寫Scala程序。 正則表達式

若能給一樣對Scala感興趣的IT人一些幫助,本人必感欣慰。 算法

——JamesQiu

jamesqiu@sina.com

http://qiujj.com

目錄

Scala 2.8+ Handbook

1.           Scala有多cool

1.1.        速度!

1.2.        易用的數據結構

1.3.        OOP+FP

1.4.        動態+靜態

1.5.        DSL

1.6.        夠複雜

1.7.        夠有趣

1.8.        開發社區

2.           lang

2.1.        Java的異同

2.2.        變量

2.3.        基本類型

2.4.        BigInt

2.5.        字符串

2.6.        Null, None, Nil, Nothing

2.7.        ==eq

2.8.        Option[T]

2.9.        區分<-,=>,->

2.10.      match..case(switch)

2.11.      try..catch..finally

2.12.      require

2.13.      main方法

2.14.      package, import

2.15.      if..else

2.16.      循環操做

2.17.      操做符重載

2.18.      系統定義scala._

2.19.      implicit隱式轉換

2.20.      typealias

2.21.      泛型

2.22.      枚舉Enum

3.           FP

3.1.        函數

3.2.        函數式編程

4.           OOP

4.1.        class

4.2.        trait超級接口

4.3.        協變和逆變(co-|contra-)variance

5.           util

5.1.        架構

5.2.        集合Array,List,Tuple

5.3.        Map

5.4.        Set

5.5.        Iterator

5.6.        Paralllel collection

6.           io

6.1.        文件I/O

6.2.        網絡I/O

7.           actor

7.1.        actor模型

7.2.        多核計算

7.3.        Actor用法

7.4.        方式1:接受receive

7.5.        方式2:接受react, loop

7.6.        REPL接受消息

7.7.        actor最佳實踐

7.8.        不一樣jvm間的消息訪問

7.9.        STM

8.           misc

8.1.        xml

8.2.        json

8.3.        Configgy

8.4.        正則表達式regex

8.5.        GUI

9.           附錄

9.1.        stackoverflowscala教程

9.2.        ProjectEuler

9.3.        Scala問答

9.4.        rosettacode

9.5.        編譯、mvnSBT

9.6.        Scala水平劃分

9.7.        Scala適合的領域

9.8.        TwitterScala School

 

 

1.   Scala有多cool

「Put productivity & creativity back in the hands of developers」

Scala is a tour de force of language design.

 

1.1.     速度!

Java的運行速度

——基於JVM,和Java運行速度至關。看看RubyPerlPython對大項目運行效率的無奈,就知道有個好的編譯器(Scalac)和運行時(JVM)是多麼美好。

Python/Ruby的編程速度

——有更多的內建庫和數據結構,編程就更快,Scala在徹底繼承Java.NET的標準庫的基礎上,還擴展了更豐富有用的函數庫。看看C++DGo等語言庫的發展狀況(不是匱乏就是混亂),就知道從頭建立如Java.NET這般龐大全面的類庫並不是易事;

類庫和運行速度有關係嗎?——很大程度上有,衆多專家已經在類庫中準備了充分優化的穩定算法,ScalaJava Collection算法進行直接包裝或者直接調用,若是沒有豐富的類庫,你在項目週期內免不了摘抄一些不必定靠譜的算法和功能代碼,這些代碼極有可能在運行時給你帶來麻煩。使用類庫算法,不用擔心自造輪子的運行效率。

Scala是靜態語言,ScalacJavac是同一做者,編譯成.class後運行於JVM平臺,近20年那麼多大公司投入進行的優化也不是白搭。對於大部分的應用來講,使用Scala不用再顧慮運行速度,它可能不是最快,但至少逼近Java,而不像GroovyJRubyJython那般與Java有高達數十倍的效率差距。

1.2.     易用的數據結構

List-Map-Tuple及其豐富特性支持讓你解決數據結構問題時遊刃有餘。

List(1,31,4,3,53,4,234) filter (10<) filter (100>)  // List(31, 53)
val (a,b) = List(1, 31,4,3,53,4,234) partition (10>) // a=List(1,4,3,4), b=List(31,53,234)
def info(p:Person) = (name, age, email) // info._1, info._2, info._3

1.3.     OOP+FP

l  適當地選用OOP或者FP,可以使表達相對另外一種更加清晰準確。

l  實際可見的生產力在於:一個應用中的部分代碼尤爲是高知識凝聚的代碼如數學函數和設計模式,通常來講不會本身編寫,而是會來自於現成的Java庫,或者其餘語言,或者僞代碼。咱們能夠很容易地把過程語言、面嚮對象語言、函數式語言中的代碼「翻譯」成Scala代碼。試想若是咱們要把Haskell或者Lisp的某個尾遞歸算法翻譯成Java代碼,還得多花點時間;而要把C++的代碼翻譯成Hashkell,一樣也不簡單。Scala的混血性給咱們的實際使用提供了便利。

l  語言特點可以塑造編程者的思惟: C++也能使用抽象基類設計多重繼承,但Java的接口引導你走得更遠;Java也能設計類型安全的靜態方法(final static),但Scala鼓勵你這樣作並逐步從OOP到達FP的彼岸,並且來去自如。

1.4.     動態+靜態

Scala雖然是一門徹頭完全的靜態語言,但又具有了現代動態語言的不少方便和靈活:

l  不須要冗餘的類型聲明

l  能夠在已有類上增長新方法(implicit轉換和Dynamic trait

l  能夠把不繼承共同父類的不一樣類型對象傳到方法中

l  能夠作靜態語言的Refactoring

l  不用象動態語言那樣測試代碼比業務代碼還多

l  代碼自動完成(REPLIDE

l  編譯靜態語言的性能

l  Read-Eval-Print Loop交互解釋器(注:Linux下的用戶體驗遠好於Windows下)

1.5.     DSL

Scala能夠把xml/html處理、數學公式表達、SQL查詢等包裝的更優雅、更合理,爲使用者提供更好的API。這也使Scala的程序也更可讀,從而更易於維護。

1.6.     夠複雜

不一樣的思考模式:Java是先寫後想,Scala是先想後寫(其實FP大都如此)。

Scala相比於Java,可能達不到10倍的代碼精簡;但讀Scala代碼的效率通常只有Java1/10——可見Java是一門沒有多少特例的簡單語言,而Scala則否則。

你不要期望把Scala做爲初學者的第一門編程語言,這門語言甚至不是初級程序員可以掌控的——換句話說,可以讀懂和寫Scala代碼,說明你是一個徹徹底底的資深程序員,或者更準確一點,是資深Java程序員。

ð   

簡單的JavaMorse碼,定型的玩具車)

複雜的Scala(智能機,樂高積木)

 

 

1.7.     夠有趣

還看這句話:「Put productivity & creativity back in the hands of developers」。其實不只限於Scala,對於全部的編程語言來講,一門語言是否「好玩」有趣,可否激起創做欲,纔是最關鍵的,這比語言風格、運行速度、工具支持、社區文化都來得重要。

回想我使用過的語言,CC++,只有在學習「圖形學」課程作做業的時候給我「好玩」和編程過癮的感受;VBDelphiLotus Script/FormularJavaScript,重來沒有給過我「好玩」的感受,而Java是在以前很長一段時間內讓我以爲最「好玩」的語言,用它編遊戲、作模式識別的做業、作產品……,樂在其中。可是Java也許久沒有再給我這種編程過癮的感受了。以前發現Groovy的時候,我覺得又找到一門好玩的語言了,但我一段時間使用以後,發現不是個人菜(PerlPythonRuby也如此);我不是說那些我不以爲好玩的語言很差,有其餘不少人以爲他們很是「好玩」,而且用它們建立了無數殺手級的、偉大的、有用的程序。

有些人對一門語言會玩一生,就像LispHaskellSmalltalk的擁躉;而有些人會不斷尋找下一個玩意兒,就像原來玩Java的一些人發現更好玩的RubyPython以後,倒戈狂噴Java,力挺後者;Groovy/Grails的玩家在很短的時間裏面,寫了無數的擴展和Plugin應用;學習Scala,能不少好玩的地方,能用它有激情地去寫一些振奮人心的應用出來!

1.8.     開發社區

Scala開發/用戶社區氣氛良好,基本都是資深開發者以及有必定經驗的用戶,不會碰到太弱智的事(提問、爭吵),除了語言和工具開源免費,最權威和最好的書也都是免費的(包括Lift社區)

 

2.   lang

2.1.     Java的異同

2.1.1.  語法

Java++:增長的語法

Java--:刪減的語法

OO

靜態成員

操做符重載

原生數據類型

closure

breakcontinue

使用trait進行mixin組合

接口

existential type_

通配符List<?>, import pkg.*;

抽象類型 type T

原始類型 class C1<T> {...}

模式匹配

enum枚舉

注:

existential type——和Java互操做時進行對應

Iterator<? extends Component>  -->  Iterator[T] { type T <: Component }或者Iterator[_]

 

2.1.2.  

如下功能經過庫的形式提供:

l  assert

l  enum

l  property

l  event

l  actor

l  resource control(自動釋放)

def using[T <: { def close() }] (res:T)(block:T=>Unit) = {

  try { block(res) } finally { if(res!=null) res.close }}

using (new BufferedReader(new FileReader(path))) { f=> println(f.readLine) }

不用每次使用Java中的finally

val f = new BufferedReader(new FileReader(path)

try { println(f.readLine) } finally { if (f!=null) f.close }

l  query

2.2.     變量

2.2.1.  保留字

abstract   case       catch      class      def

do         else       extends    false      final

finally    for        if         implicit   import

match      new        null       object        override

package    private    protected requires   return

sealed        super      this       throw      trait

try        true       type       val        var

while      with       yield

_      :      =      =>     <-     <:     <%     >:     #      @

 

Scala調用Java的方法時,會碰到有Scala的保留字,如Thread.yield()

這在Scala中是非法的,專門有個解決辦法,寫成: Thread.`yield`()

 

注意:沒有breakcontinue

2.2.2.  變量標識

這些標識在Java中是非法的,在Scala中是合法的,能夠看成函數名使用,使接口更加DSL

val empty_? = true

val + = "hello"

val `yield` = 10

val ** = "power"

2.2.3.  變量定義

2.2.3.1     val, var

var 可變,可從新賦值,賦值爲"_"表示缺省值(0, false, null),例如:

var d:Double = _ // d = 0.0

var i:Int = _ // i = 0

var s:String = _ // s = null

var t:T = _  // 泛型T對應的默認值

 

val 不可變,至關於const/final,但若是val爲數組或者Listval的元素能夠賦值;

val pi = 3. // 至關於3.0d

val pi = 3.f // 至關於3.0f

 

提示:向函數式風格推動的一個方式,就是嘗試不用任何var來定義變量。

2.2.3.2     花樣定義

Python同樣方便的賦值方式:

val x,y = 0 // 賦同一初始值

val (x,y) = (10, "hello") // 同時定義多個變量,注意:val x,y=10,"hello" 錯誤

更花:

val x::y = List(1,2,3,4)  // x = 1, y = List(2,3,4)

val List(a,b,c) = List(1,2,3) // a = 1, b = 2, c = 3

進一步花樣:

val Array(ab_, _, c @ _*) = Array(1234, 567)  // 也能夠用ListSeq

a // 1

b // 2

c // Array(5, 6, 7), _*匹配0個到多個

 

使用正則表達式定義:

val regex = "(\\d+)/(\\d+)/(\\d+)".r

val regex(year, month, day) = "2010/1/13"

// year: String = 2010

// month: String = 1

// day: String = 13

2.2.3.3     lazy, val, def的區別

val

定義時就一次求值完成,保持不變

val f = 10+20 // 30 

lazy

定義時不求值,第一次使用時完成求值,保持不變

lazy f = 10+20 // <lazy>

f // 30

def

定義時不求值,每次使用時都從新求值

def f = 10+20 // 30

def t = System. currentTimeMillis // 每次不同

scala> val f1 = System.currentTimeMillis

f1: Long = 1279682740376    // 立刻求值

scala> f1

res94: Long = 1279682740376 // 以後保持不變

scala> lazy val f2 = System.currentTimeMillis

f2: Long = <lazy>  // 定義時不求值

scala> System.currentTimeMillis

res95: Long = 1279682764297

scala> f2

res96: Long = 1279682766545 // 第一次使用時求值,注意:6545 > 4297

scala> f2

res97: Long = 1279682766545 // 以後保持不變

scala> def f3 = System.currentTimeMillis

f3: Long

scala> f3

res98: Long = 1279682784478 // 每次求值

scala> f3

res99: Long = 1279682785352 // 每次求值

2.3.     基本類型

儘可能使用大寫形式: Int, Long, Double, Byte, Short, Char, Float, Double, Boolean

編譯時Scala自動對應到Java原始類型,提升運行效率。Unit對應javavoid

 

 asInstanseOf[T]方法來強制轉換類型:

def i = 10.asInstanceOf[Double] // i: Double = 10.0

List('A','B','C').map(c=>(c+32).asInstanceOf[Char]) // List('a','b','c')

 

isInstanceOf[T]方法來判斷類型:

val b = 10.isInstanceOf[Int] // true

而在match ... case 中能夠直接判斷而不用此方法。

 

Any統一了原生類型和引用類型。

 

2.3.1.  Int

-3 abs // 3

-3 max -2 // -2

-3 min -2 // -3

1.4 round // 1 四捨五入

1.6 round // 2 四捨五入

1.1 ceil // 2.0 天花板

1.1 floor // 1.0 地板

 

++--操做,但能夠+=, -=, 以下:

var i = 0

i++  // 報錯,無此操做

i+=1 // 1

i--  // 報錯,無此操做

i-=1 // 0

 

def even(n:Int) = 0==(n & 1)

def odd(n:Int) = !even(n)

2.3.2.  Char

String能夠轉化爲List[Char]

String上作循環,其實就是對String中的每個Char作操做,如:

"12345" map (toInt) // (49,50,51,52,53)

"jamesqiu" max // 'u'

"jamesqiu" min // 'a'

('a' to 'f') map (_.toString*3) // (aaa, bbb, ccc, ddd, eee, fff)

2.4.     BigInt

能夠表示很大的整數:

BigInt(10000000000000000000000000) // 報錯

BigInt("10000000000000000000000000") // scala.math.BigInt = 10000000000000000000000000

 

例如:

def  fac(n:Int):BigInt = if (n==0) 1 else fac(n-1)*n

fac(1000)

或者寫成:

def fac2(n:Int) = ((1:BigInt) to n).product

// res1: BigInt = 9332621544394415268169923885626670049071596826438......000000000000000000

2.5.     字符串

"..." 或者 """...""""

 

println("""|Welcome to Ultamix 3000.  

           |Type "HELP" for help.""".stripMargin)

輸出:

Welcome to Ultamix 3000.  

Type "HELP" for help.

 

scala中,字符串除了能夠+,也能夠*

"abc" * 3 // "abcabcabc"

"abc" * 0 // ""

 

例子:

"google".reverse // "elgoog"

"abc".reverse.reverse=="abc" // true

 

例子:

"Hello" map (_.toUpper) // 至關於 "Hello".toUpperCase

2.5.1.  類型轉換

"101".toInt // 101,無需 Integer.parseInt("101");

"3.14".toFloat // 3.14f

101.toString

3.14.toString

轉換整個列表:

List("1","2","3") map (_.toInt) // List(1,2,3)

或者

List("1","2","3") map Integer.parseInt // List(1,2,3)

 

2.5.2.  StringBuilder

val sb = new StringBuilder

sb += 'H'

sb ++= "ello"

sb.toString // "Hello"

sb clear // StringBuilder()

2.5.3.  文本格式化

使用java.text.MessageFormat.format:

val msg = java.text.MessageFormat.format(
    "At {1,time} on {1,date}, there was {2} on planet {0}.",
    "Hoth", new java.util.Date(), "a disturbance in the Force")

輸出

At 17:50:34 on 2010-7-20, there was a disturbance in the Force on planet Hoth.

 

方法2

"my name is %s, age is %d.format ("james", 30) // my name is james, age is 30.

 

注意:format還能夠這麼用

"%s-%d%1$s is %2$d.format ("james", 30) // james-30james is 30.

    "%2$d age's man %1$s: %2$dformat ("james", 30) // 30 age's man james: 30

 

2.6.     Null, None, Nil, Nothing

Null

Trait,其惟一實例爲null,是AnyRef的子類,*不是* AnyVal的子類

Nothing

Trait,全部類型(包括AnyRefAnyVal)的子類,沒有實例

None

Option的兩個子類之一,另外一個是Some,用於安全的函數返回值

Unit

無返回值的函數的類型,和javavoid對應

Nil

長度爲0List

 

2.7.     ==eq

Scala==很智能,他知道對於數值類型要調用Java中的==ref類型要調用Javaequals()

"hello"=="Hello".toLowerCase()

java中爲false,在scala中爲true

 

Scala==老是內容對比

基本類型IntDouble

比值

其餘類型

至關於A.equals(B)

eq纔是引用對比

例如:

val s1,s2 = "hello"

val s3 = new String("hello")

s1==s2 // true

s1 eq s2 // true

s1==s3 // true 值相同

s1 eq s3 // false 不是同一個引用

2.8.     Option[T]

2.8.1.  概念

l  Option[T]能夠是任意類型或者空,但一旦聲明類型就不能改變;

l  Option[T]可完美替代Java中的null,能夠是Some[T]或者None

l  Option實現了map, flatMap, and filter 接口,容許在 'for'循環裏使用它;

 

函數返回值能被統一處理了:

沒有Option的日子

如今

def find(id:Long):Person = ...

def find(id:Long):Option[Person] = ...

返回Person或者null

返回Some[Person]或者None

返回null不特殊處理會拋:NullPointerExceptions

返回值直接getOrElse或者列表操做

類比:JavaStringx.split返回null

類比:JavaStringx.split返回new String[0]

結論:函數永遠不要返回null值,若是輸入有問題或者拋異常,返回Option[T]

 

參數有效性檢查沒有那麼煩人了:

沒有Option的日子

如今

def blank(s:String) = if (s==null) false else{

  s.toList.forall(_.isWhitespace) }

def blank(s:String) =Option(s).toList.forall(

_.forall(_.isWhitespace))

結論:儘量地不要浪費代碼去檢測輸入,包裝成Option[T]來統一處理

 

2.8.2.  使用

Some(3).getOrElse(4) // 3

None.getOrElse(4) // 4

例如打印key=3value

寫法1

def p(map:Map[Int,Int]) = println(map(3))

p(Map(1->100,2->200)) // 拋異常

寫法2

def p(map:Map[Int,Int]) = println(map get 3 getOrElse "...")

p(Map(1->100,2->200)) // ...

p(Map(1->100,3->300)) // 300

 

2.8.3.  例子

例子1

  def m(k:Int) = {

    Map((1,100),(2,200),(3,300)) get(kmatch {

      case Some(v) =>

        k + ": " + v

      case None =>

        "not found"

    }

  }

 

  def main(args : Array[String]) : Unit = {

    println(m(1)) // 100

    println(m(2)) // 200

    println(m(3)) // 300

    println(m(4)) // "not found"

    println(m(-1)) // "not found"

  }

 

例子2

val l = List(Some(100), None, Some(200), Some(120), None)

for (Some(s) <- l) yield s // List(100, 200, 120)

l flatMap (x=>x) // List(100, 200, 120)

 

例子3 Option結合flatMap

def toint(s:String) =

try { Some(Integer.parseInt(s)) } catch { case e:Exception => None }

List("123", "12a", "45") flatMap toint // List(123, 45)

List("123", "12a", "45") map toint // List(Some(123), None, Some(45))

 

2.9.     區分<-,=>,->

<-

for (i <- 0 until 100)

用於for循環, 符號的象形

=>

List(1,2,3).map(x=> x*x)

((i:Int)=>i*i)(5) // 25

用於名函數,至關於Ruby |x|,Groovy{x-> x*x}

也可用在import中定義別名:import javax.swing.{JFrame=>jf}

->

Map(1->"a",2->"b")

用於Map初始化也能夠不用->而寫成 Map((1,"a"),(2,"b"))

 

2.10.    match..case(switch)

2.10.1.      switch..case的區別

Java裏面的寫法

switch(n) {

    case(1): ...; break;

    case(2): ...; break;

    default: ...;

}

變成Scala寫法

def m(n:String) =

match {

    case "a" | "b" => ... // 這個比較好

    case "c" => ...

    case _ => ...

}

每一個case..=>結束不用寫break_至關於default

 

2.10.2.      匹配數據類型

match 能夠很簡單地匹配數據類型(不須要isInstanceOf[T]):

def f(v:Any) = v match {

    case null => "null"

    case i:Int => i*100

    case s:String => s

    case _ => "others"

}

注意:上面case中的is都叫模式變量

f(null) // "null"

f(5) // 500

f("hello") // "hello"

f(3.14) // "others"

 

注意:自定義類型若是也要匹配,須要用case class

2.10.3.      命令行參數解析例子

/** Basic command line parsing. */
object Main {
  var verbose = false  // 記錄標識,以便能同時對-h-v作出響應
 
  def main(args: Array[String]) {
    for (a <- args) a match {
      case "-h" | "-help"    =>
        println("Usage: scala Main [-help|-verbose]")
      case "-v" | "-verbose" =>
        verbose = true
      case x => // 這裏x是臨時變量
        println("Unknown option: '" + x + "'")
    }
    if (verbose) println("How are you today?")
  }
}

2.10.4.      使用case的遞歸函數

寫法1

def fac(n:Int):Int = n match {

  case 0=>1

  case _=>n*fac(n-1)

}

 

寫法2(使用函數):

def fac: Int=>Int = {

case 0=> 1

case n=> n*fac(n-1)

}

 

寫法3(使用尾遞歸):

  def fac: (Int,Int)=>Int = {

    case (0,y) => y

    case (x,y) => fac(x-1, x*y)

  }
fac(5,1) // 120
 
寫法4reduceLeft+!):
def fac(n:Int) = 1 to n reduceLeft(_*_)
implicit def foo(n:Int) = new { def ! = fac(n) }
5! // 120
 
寫法5(最簡潔高效)
  def fac(n:Int) = (1:BigInt) to n product
  fac(5) // 120

2.10.5.      變量匹配

常量匹配很簡單,即case後跟的都是常量;
變量匹配須要注意,case後跟的是match裏面的臨時變量,而不是其餘變量名:
3 match {
case i => println("i=" + i) // 這裏i模式變量(臨時變量),就是3
}
val a = 10
20 match { case a => 1 } // 1 a模式變量,不是10
爲了使用變量a,必須用`a`:
        20 match { case `a` => 1; case b => -1 } // -1`a`是變量10
或者用大寫的變量:
        val A = 10
        20 match { case A => 1; case b => -1 } // -1,大寫A是變量10
 

2.10.6.      case..if條件匹配

寫法1
(1 to 20) foreach {                              case x if (x % 15 == 0) => printf("%2d:15n\n",x)     case x if (x % 3 == 0)  => printf("%2d:3n\n",x)     case x if (x % 5 == 0)  => printf("%2d:5n\n",x)     case x => printf("%2d\n",x)                          }
寫法2
(1 to 20) map (x=> (x%3,x%5) match {
  case (0,0) => printf("%2d:15n\n",x)
  case (0,_) => printf("%2d:3n\n",x)
  case (_,0) => printf("%2d:5n\n",x)
  case (_,_) => printf("%2d\n",x)
})

2.11.    try..catch..finally

var f = openFile()

try {

  f = new FileReader("input.txt")

catch {

  case ex: FileNotFoundException => // Handle missing file

  case ex: IOException => // Handle other I/O error

finally {

  f.close()

}

2.12.    require

def f(n:Int) = { require(n!=0); 1.0/n }

def f(n:Int) = { require(n!=0, "n can't be zero"); 1.0/n }

f(0)

// java.lang.IllegalArgumentException: requirement failed: n can't be zero

 

2.13.    main方法

Scalamain方法(包括全部相似javastatic方法)必須定義在一個object內:

object Test1 {

    def main(args: Array[String]) {

       println("hello world")

    }

}

編譯:

fsc Test1.scala // 常駐內存編譯服務器,第一次轉載以後比scalac

運行:

scala Test1.scala // 方式1,沒有輸出

scala -cp e:\scala\lib\scala-library.jar Test1 // 方式2,慢

java -cp e:\scala\lib\scala-library.jar Test1 // 方式3,快

 

若是文件不是utf8編碼,執行須要使用.scala文件的編碼,如:

scala -encoding gbk test.scala

2.13.1.      Application

不帶命令行參數的簡化main方法:

object app1 extends Application {

    println("hello world")

}

2.14.    package, import

2.14.1.      import

Scalaimport能夠只在局部做用域內生效;

能夠格式 import javax.swing.{JFrame=>jf}」來聲明類型的別名。

jf.show()

l  import javax.swing._

l  import java.util.{List, Map}

l  import java.util._, java.io._

Scala 缺省導入以下包:

l  java.lang.*

l  scala.*

l  scala.Predef

 

因爲Scalapackage能夠是相對路徑下定義,有可能命名衝突,能夠用:

import _root_.java.lang.Long

2.14.2.      package

package com.wr3 { // C# Ruby的方式,也能夠改用Java的方式

    // import java.nio._ // "*" scala的正常函數名,因此用_

    class c1 {

       def m1() { println("c1.m1()") }

    }

 

    object o1 {

       def main(args: Array[String]) {

          println("o1.main()")

          new c1().m1()

        }

    }

}

編譯:

fsc package.scala

運行:

java com.wr3.o1 // 方式1

scala com.wr3.o1 // 方式2

 

2.14.3.      包對象

Scala2.8+支持包對象(package object),除了和2.8以前同樣能夠有下級的objectclass,還能夠直接有下級變量和函數,例如:

-------------------------------- foo.scala

package p0

package object p1 {

    val a = 10

    def b = "hello " + a

    def main(args:Array[String]):Unit = printf("%s", p0.p1.b)

}

--------------------------------

p1就是一個包對象,ab就是包p1直屬的常量和函數,

$fsc foo.scala 命令產生以下class

./p0/p1/package.class

調用:

scala p0.p1.package

2.15.    if..else

沒有java的:

b = (x>y) ? 100 : -1

就用:

if (x>y) 100 else -1

2.16.    循環操做

map

m->m

flatMap

m->n

indices

m->m

foreach

m->Unit

for (... if ...) yield

m->n

collect { case ... if ... => ... }

m->n

filter, filterNot

m->n

take

m->n

takeWhile

m->n

forall

m->1 (true|false)

reduceLeft, foldLeft

m->1

scanLeft

m->m+1

exists

m->1 (true|false)

find

m->1 (或者None)

count

m->1

span, partition

m->2

 

2.16.1.      for

循環中的變量不用定義,如:

for(i<-1 to 10; j=i*i) println(j)

for (s <- ss) foo(s)

for (i <- 0 to n) foo(i) // 包含n,即Range(0,1,2,...,n,n+1)

for (i <- 0 until n) foo(i)  // 不包含n,即Range(0,1,2,3,...,n)

例如:

for (n<-List(1,2,3,4) if n%2==1) yield n*n  // List(1, 9)

for (n<-Array(1,2,3,4) if n%2==1) yield n*n  // Array(1, 9)

注意:若是if後面不止一條語句,要用{..}包裹。

var s = 0; for (i <- 0 until 100) { s += i } // s = 4950

 

等價於不用for的寫法:

List(1,2,3,4).filter(_%2==1).map(n => n*n)

 

若是for條件是多行,不能用(),要用{}

for(i<-0 to 5; j<-0 to 2) yield i+j

// Vector(0, 1, 21, 2, 32, 3, 43, 4, 54, 5 , 65, 6, 7)

for{i<-0 to 5

j<-0 to 2} yield i+j

 

例子1

// 邊長21之內全部符合勾股弦的三角形:

def triangle(n: Int) = for {   x <- 1 to 21   y <- x to 21   z <- y to 21   if x * x + y * y == z * z } yield (x, y, z)

結果:

// Vector((3,4,5), (5,12,13), (6,8,10), (8,15,17), (9,12,15), (12,16,20))

 

2.16.1.    for .. yield

把每次循環的結果「」進一個集合(類型和循環內的一致)

for {子句} yield {循環體}

 

正確:

for (e<-List(1,2,3)) yield (e*e) // List(1,4,9)

for {e<-List(1,2,3)} yield { e*e } // List(1,4,9)

for {e<-List(1,2,3)} yield e*e // List(1,4,9)

 

錯誤:

for (e<-List(1,2,3)) { yield e*e } // 語法錯誤,yield不能在任何括號內

 

2.16.2.      foreach

List(1,2,3).foreach(println)

1

2

3

 

也能夠寫成:

(1 to 3).foreach(println)

或者

(1 until 4) foreach println

或者

Range(1,3) foreach println

 

注意:

l  to包含,until不包含(最後的數)

l  均可以寫步長,如:

1 to (11,2) // 1,3,5,7,9,11 步長爲2

    1 to 11 by 2

1 until (11,2) // 1,3,5,7,9

1 until 11 by 2

val r = (1 to 10 by 4) // (1,5,9), r.start=r.first=1; r.end=10, r.last=9

l  也能夠是BigInt

(1:BigInt) to 3

2.16.3.      forall

"全部都符合"——至關於 A1 && A2 && A3 && ... && Ai && ... && An

(1 to 3) forall (0<) // true

(-1 to 3) forall (0<) // false

 

又如:

def isPrime(n:Int) = 2 until n forall (n%_!=0)

for (i<-1 to 100 if isPrime(i)) println(i)

(2 to 20) partition (isPrime _) // (2,3,5,7,11,13,17,19), (4,6,8,9,10,12,14,15,16,18,20)

也可直接調用BigInt的內部方法:

(2 to 20) partition (BigInt(_) isProbablePrime(10))

// 注:isProbablePrime(c)c越大,是質數的機率越高,10對應機率:1 - 1/(2**10) = 0.999

 

2.16.4.      reduceLeft

reduceLeft 方法首先應用於前兩個元素,而後再應用於第一次應用的結果和接下去的一個元素,等等,直至整個列表。例如

計算階乘:

def fac(n: Int) = 1 to n reduceLeft(_*_)

fac(5) // 5*4*3*2 = 120

至關於:

    ((((1*2)*3)*4)*5)

計算sum

    List(2,4,6).reduceLeft(_+_) // 12

至關於:

    ((2+4)+6)

max

List(1,4,9,6,7).reduceLeft( (x,y)=> if (x>y) x else y ) // 9

       或者簡化爲:

List(1,4,9,6,7).reduceLeft(_ max _) // 9

至關於:

    ((((1 max 4) max 9) max 6) max 7)

2.16.5.      foldLeft scanLeft

 

累加或累乘

def sum(L: List[Int]): Int = {

        var result = 0

        for (item <- L)

            result += item

        result

}

scalable的寫法:

def sum(L: Seq[Int]) = L.foldLeft(0)((a, b) => a + b)

def sum(L: Seq[Int]) = L.foldLeft(0)(_ + _)

def sum(L: List[Int]) = (0/:L){_ + _}

調用:

sum(List(1,3,5,7)) // 16

 

乘法:

def multiply(L: Seq[Int]) = L.foldLeft(1)(_ * _)

multiply(Seq(1,2,3,4,5)) // 120

multiply(1 until 5+1) // 120

 

2.16.6.      scanLeft

List(1,2,3,4,5).scanLeft(0)(_+_) // (0,1,3,6,10,15)

至關於:

(0,(0+1),(0+1+2),(0+1+2+3),(0+1+2+3+4),(0+1+2+3+4+5))

 

List(1,2,3,4,5).scanLeft(1)(_*_) // (1,2,6,24,120)

至關於

(1, 1*1, 1*1*2, 1*1*2*3, 1*1*2*3*4, 1*1*2*3*4*5)

 

注:

l  (z /: List(a, b, c))(op至關於 op(op(op(z, a), b), c)

簡單來講:加法用0,乘法用1

l  (List(a, b, c) :\ z) (op) equals op(a, op(b, op(c, z)))

 

2.16.7.      take drop splitAt

1 to 10 by 2 take 3 // Range(1, 3, 5)

1 to 10 by 2 drop 3 // Range(7, 9)

1 to 10 by 2 splitAt 2 // (Range(1, 3),Range(5, 7, 9))

 

例子:前10個質數

def prime(n:Int) = (! ((2 to math.sqrt(n).toInt) exists (i=> n%i==0)))

2 to 100 filter prime take 10

2.16.8.      takeWhile, dropWhile, span

while語句的縮寫,

takeWhile (...)

等價於:while (...) { take }

dropWhile (...)

等價於:while (...) { drop }

span (...)

等價於:while (...) { take; drop }

 

1 to 10 takeWhile (_<5) // (1,2,3,4)

1 to 10 takeWhile (_>5) // ()

10 to (1,-1) takeWhile(_>6) // (10,9,8,7)

1 to 10 takeWhile (n=>n*n<25) // (1, 2, 3, 4)

若是不想直接用集合元素作條件,能夠定義var變量來判斷:

例如,從1 to 10取前幾個數字,要求累加不超過30

var sum=0;

val rt = (1 to 10).takeWhile(e=> {sum=sum+e;sum<30}// Range(1, 2, 3, 4, 5, 6, 7)

注意:takeWhile中的函數要返回Booleansum<30要放在最後;

 

1 to 10 dropWhile (_<5) // (5,6,7,8,9,10)

1 to 10 dropWhile (n=>n*n<25) // (5,6,7,8,9,10)

 

1 to 10 span (_<5) // ((1,2,3,4),(5,6,7,8)

List(1,0,1,0) span (_>0) // ((1), (0,1,0))

注意,partition是和span徹底不一樣的操做

List(1,0,1,0) partition (_>0) // ((1,1),(0,0))

2.16.9.      breakcontinue

Scala沒有breakcontinue語法!須要break得加輔助boolean變量,或者用庫(continue沒有).

 

例子1:打印'a' to 'z'的前10

var i=0; val rt = for(e<-('a' to 'z') if {i=i+1;i<=10}) printf("%d:%s\n",i,e)

或者:

('a' to 'z').slice(0,10).foreach(println)

 

例子21 to 100 和小於1000的數

var (n,sum)=(0,0); for(i<-0 to 100 if (sum+i<1000)) { n=i; sum+=i }

// n = 44, sum = 990

 

例子3:使用庫來實現break

import scala.util.control.Breaks._

for(e<-1 to 10) { val e2 = e*e; if (e2>10) break; println(e) }

 

2.17.    操做符重載

注意:其實Scala沒有操做符,更談不上操做符重載;+-/*都是方法名,如1+2實際上是(1).+(2)

object operator {

    class complex(val i:Int, val j:Int) { // val 是必須的

        def + (c2: complex) = {

            new complex (i+c2.i, j+c2.j)

        }

        override def toString() = { "(" + i + "," + j + ")" }

    }

    def main(args:Array[String]) = {

        val c1 = new complex(3, 10)

        val c2 = new complex(5, 70)

        printf("%s + %s = %s", c1, c2, c1+c2)

    }

}

編譯:fsc operator.scala

運行:java operator // (3,10) + (5,70) = (8,80)

 

2.18.    系統定義scala._

scala._自動加載,只有發生類名衝突時才須要帶上scala.包名。

scala.AnyValue

全部基本類型的根

Int,Char,Boolean,Double,Unit

scala.AnyRef

全部引用類型的根

至關於javajava.lang.Object

scala.Null

全部引用類型的子類

 

scala.Nothing

全部所有類型的子類

 

scala.List

不可變的List

scala特點的不可變List

scala.Int

scala中能夠用int做爲別名

DoubleFloat等相似

 

2.19.    implicit隱式轉換

用途:

l  把一種object類型安全地自動轉換成另外一種object類型;

l  不改動已有class設計便可添加新的方法;

2.19.1.      類型轉換

implicit def foo(s:String):Int = Integer.parseInt(s) // 須要時把String->Int

def add(a:Int, b:Int) = a+b

add("100",8) // 108, 先把"100"隱式轉換爲100

2.19.2.      例子:階乘n!

第一步:寫函數

def factorial(x: Int) = 1 to n reduceLeft(_*_)

 

第二步:定義 "!" 函數

    class m1(n: Int) {

        def ! = factorial(n)

    }

implicit def m2(n:Int) = new m1(n) // 隱式轉換,即在須要時n轉換爲new m1(n)

 

注意:上面能夠用匿名類簡化爲:

    implicit def m2(n:Int) = new { def ! = factorial(n) }

 

第三步:使用

    val n = 100

printf("%d! = %s\n", n, (n!))  // n! 至關於 new m1(n).!()

println(10!)

 

2.19.3.      例子:cout

import java.io._

class C1(p:PrintStream) {

def << (a:Any) = {

p.print(a)

p.flush

p

}

}

implicit def foo(p:PrintStream) = new C1(p)

val endl = '\n'

System.out<<"hello"<<" world"<<endl

2.19.4.      例子:定義?:

implicit def elvisOperator[T](alt: T) = new {
  def ?:[A >: T](pred: A) = if (pred == null) alt else pred
}                  

 

null ?: "" // ""

"abc" ?: "" // "abc"

10 ?: 0 // 10

(null ?: 0).asInstanceOf[Int] // 0

2.19.5.      已有Object加新方法

object NewMethod {

  // 定義新方法join()

  implicit def foo1[T](listList[T]) = new {

    def join(s:String) = list.mkString(s)

  }

  // 測試

  def main(args : Array[String]) : Unit = {

    Console println List(1,2,3,4,5).join(" - "// " 1 - 2 - 3 - 4 – 5"

  }

}

解釋:

編譯器發現List沒有join(String)方法,就發查找代碼中有沒有定義在implicit def xx(List)內的 join(String)方法,若是有就調用這個。

 

Int增長乘方操做:

def pow(n:Int, m:Int):Int = if (m==0) 1 else n*pow(n,m-1)

implicit def foo(n:Int) = new {

  def **(m:Int) = pow(n,m)

}

2**10 // 1024

 

例子2:定義如ruby10.next

implicit def foo(n:Int) = new { def next = n+1 }

10.next // 11

 

2.20.    typealias

至關於C語言的類型定義typedef,創建新的數據類型名(別名);在一個函數中用到同名類時能夠起不一樣的別名

例如:

type JDate = java.util.Date       

type SDate = java.sql.Date

val d1 = new JDate()  // 至關於 val d = new java.util.Date()

val d2 = new SDate()  // 至關於 val d = new java.sql.Date()

 

注意:type也能夠作泛型

 

 

2.21.    泛型

2.21.1.      函數中的泛型:

def foo[T](a:T) = println("value is " + a)

foo(10) // "value is 10"

foo(3.14) // "value is 3.14"

foo("hello") // "value is hello"

 

2.21.2.      類中的泛型:

class C1[T] {

private var v:T = _   // 初始值爲T類型的缺省值

def set(v1:T) = { v = v1 }

def get = v

}

new C1[Int].set(10).get // 10

new C1[String].set("hello").get // "hello"

2.21.3.      泛型qsort例子

def qsort[T <% Ordered[T]](a:List[T]): List[T] = if (a.size<=1) a else {

val m = a(a.size/2)

qsort(a.filter(m>)) ++ a.filter(m==) ++ qsort(a.filter(m<))

}

調用:

        val a = List(1, 3, 6, 2, 0, 9, 8, 7, 2)

        qsort[Int](a) // 注意2 a

        val b = List(1.0, 3.3, 6.2, 6.3, 0, 9.5, 8.7, 7.3, 2.2)

        qsort[Double](b) // 注意2 b

 

或者使用自帶庫:

List(1, 3, 6, 2, 0, 9, 8, 7, 2) sortWith(_<_)

List(1.0, 3.3, 6.2, 6.3, 0, 9.5, 8.7, 7.3, 2.2).sortWith(_<_)

 

2.21.4.      泛型add例子

// 採用implicit parameters

def add[T](x:T, y:T)(implicit n:Numeric[T]) = {

n.plus(x,y) // 或者用 import n._; x + y

}

add(1,2) // 3

add(1,2.14) // 3.14

 

再如,求max

def max2[T](x:T, y:T)(implicit n:Numeric[T]) = {

  import n._

  if (x > y) x else y

}

max2(2, 3.14) // 3.14

2.21.5.      泛型定義type

abstract class C1 {

    type T

    val e:T

}

 

abstract class C2 {

    type T

    val list:List[T]

    def len = list.length

}

 

def m1(e1:Int) = new C1 {

    type T = Int

    val e = e1

}

 

def m2(e1:List[Int]) = new C2 {

    type T = Int

    val list = e1

}

Console println m1(10) // 10

Console println m2(List(1,2,3,4,5)).len // 5

 

注意:type也能夠作數據類型的alias,相似C語言中的typedef

 

2.21.6.      @specialized

def test[@specialized(Int,Double) T](x:T)  = x match { case i:Int => "int"; case _ => "other" }

沒有@specialized以前,是編譯成Object的代碼;用了@specialized,變成IntTest(),DoubleTest(),...

編譯後的文件尺寸擴充了,但性能也強了,不用boxunbox

2.22.    枚舉Enum

Scala沒有在語言層面定義Enumeration,而是在庫中實現:

例子1

object Color extends Enumeration {

  type Color = Value

  val RED, GREEN, BLUE, WHITE, BLACK = Value

}

 

Color.RED // Color.Value = RED

import Color._

val colorful = Color.values filterNot (e=> e==WHITE || e==BLACK)

colorful foreach println // RED\nGREEN\nBLUE

 

例子2

object Color extends Enumeration {

  val RED = Value("紅色")

  val GREEN = Value("綠色")

  val BLUE = Value("藍色")

  val WHITE = Value("")

  val BLASK = Value("")

}

Color.RED // Color.Value = 紅色

import Color._

val colorful = Color.values filterNot (List("","") contains _.toString)

colorful foreach println //紅色\n綠色\n藍色

 

3.   FP

3.1.     函數

函數的地位和通常的變量是同等的,能夠做爲函數的參數,能夠做爲返回值。

傳入函數的任何輸入是隻讀的,好比一個字符串,不會被改變,只會返回一個新的字符串。

 

Java裏面的一個問題就是不少只用到一次的private方法,沒有和使用它的方法緊密結合;Scala能夠在函數裏面定義函數,很好地解決了這個問題。

3.1.1.  函數定義

函數和方法通常用def定義;也能夠用val定義匿名函數,或者定義函數別名

def m0(x:Int) = x*x

val m1 = (x:Int)=> x*x // ()是必須的

val m2 = {x:Int=> x*x} // 不用(), {}

m0(10) // 100

m1(10) // 100

m2(10) // 100

 

不須要返回值的函數,可使用def f() {...},永遠返回Unit(即便使用了return,即:

    def f() {...}  等價於 def f():Unit = {...}

例如:

def f() { return "hello world" }

f() // Unit,而不是 "hello world"

 

須要返回值的函數,用 def f() = {...} 或者 def f = {...}

def f() = { "hello world" }

f() // "hello world"

def f = { "hello world" } // f是匿名函數 {"hello world"}的別名

f // "hello world"

 

三種定義方式的區別:

def f() { return .. }

調用:f, f()皆可

始終返回:Unit

def f() = ...

調用:f, f()皆可

返回Unit或者值

def f = ...

調用:f

返回Unit或者值

 

提示:函數式風格——儘可能編寫有返回值的函數儘可能簡短(函數體不使用{...}

 

3.1.2.  映射式定義

一種特殊的定義:映射式定義(直接至關於數學中的映射關係);

其實也能夠當作是沒有參數的函數,返回一個匿名函數;調用的時候是調用這個返回的匿名函數。

 

例子1

def f:Int=>Double = { // 請看作 def f: (Int=>Double) = {...}

    case 1 => 0.1

    case 2 => 0.2

    case _ => 0.0

}

f(1) // 0.1

f(3) // 0.0

 

例子2

def m:Option[User]=>User = {

    case Some(x) => x

    case None => null

}

m(o).getOrElse("none...")

 

例子3(->1)

def m:(Int,Int)=>Int = _+_

m(2,3) // 5

 

例子4

def m:Int=>Int = 30+  // 至關於30+_,若是惟一的"_"在最後,能夠省略

m(5) // 35

3.1.3.  特殊函數名 + - * /

方法名能夠是*

def *(x:Int, y:Int) = { x*y }

*(10,20) // = 200

1+2, 至關於1.+(2)

 

定義一元操做符(置前)可用unary_

注:unary :一元的,單一元素的單一構成的 。發音:【`ju: ne ri

 

-2,至關於:(2).unary_- // -2

+2,至關於:(2).unary_+ // 2

!true, 至關於:(true).unary_! // false

~0,至關於 (0).unary_~  // -1

3.1.4.  缺省參數、命名參數

注意:從Scala2.8開始支持。

請對比如下兩種寫法的可讀性:

通常函數調用

命名函數調用

sendEmail(

"jon.pretty@example.com",

List("recipient@example.com"),

"Test email",

b,

Nil, Nil, Nil)

sendEmail(

    body = b,

    subject = "Test email",

    to = List("recipient@example.com"),

    from = "jon.pretty@example.com",

    attachments = List(file1, file2) )

 

定義和使用:

def join(a:List[String], s:String="-") = { a.mkString(s) }

join(List("a","b","c")) // a-b-c

join(List("a","b","c"), ":") // a:b:c

調整參數調用順序:

join(s=":",a=List("a","b","c")) // a:b:c

 

3.1.5.  函數調用

def f(s: String = "default") = { s }

f // "hello world"

f() // "hello world"

 

Ø  對象的無參數方法的調用,能夠省略.()

"hello world" toUpperCase // "HELLO WORLD"

 

Ø  對象的1個參數方法的調用,能夠省略.()

"hello world" indexOf w // 6

"hello world" substring 5 // "world"

Console print 10 // 但不能寫 print 10,只能print(10),省略Console.

1 + 2 // 至關於 (1).+(2)

 

Ø  對象的多個參數方法的調用,也可省略.但不能省略()

"hello world" substring (0, 5) // "hello"

 

注意:

l  不在class或者object中的函數不能如此調用:

def m(i:Int) = i*i

m 10 // 錯誤

l  但在class或者object中可使用this調用:

object method {

    def m(i:Int) = i*i

    def main(args: Array[String]) = {

        val ii = this m 15  // 等同於 m(15), this 不能省略

        println(ii)

    }

}

 

提示:這種調用方法最大的用途在於操做符重載,以及閱讀性強的DSL

例子

    System exit 0

    Thread sleep 10

    Console println "hello world" // 省略Console則報錯

    2 ** 10

    "-" * 100

 

3.1.6.  匿名函數

形式:((命名參數列表)=>函數實現)(參數列表)

特殊地:

l  無參數: (()=>函數實現)()

l  有一個參數且在最後: (函數實現)(參數)

l  無返回值: ((命名參數列表)=>Unit)(參數列表)

 

有參數的匿名函數(參數名稱不能省):

((i:Int)=> i*i)(3) // 9

((i:Int, j:Int) => i+j)(3, 4) // 7

 

有一個參數且在最後的:

(10*)(2) // 20, 至關於 ((x:Int)=>10*x)(2)

(10+)(2) // 12, 至關於 ((x:Int)=>10+x)(2)

(List("a","b","c") mkString)("=") // a=b=c

 

無參數的匿名函數:

(()=> 10)() // 10

 

無參數無返回值:

(() => Unit)

( ()=> {println("hello"); 20*10} )()

{ println("hello"); 20*10 }

 

例子1

def m = (i:Int)=> i*i

m(3) // 9

List(1,2,3,4).map(m) // List(1, 4, 9, 16)

 

例子2

def times3(m: ()=> Unit) = { m();m();m() }

times3 ( ()=> println("hello world") )

因爲是無參數的匿名函數,可進一步簡化:

def times3(m: =>Unit) = { m;m;m } // 參見「lazy參數」

times3 ( println("hello world") )

 

3.1.7.  偏函數

用下劃線代替1+個參數的函數叫偏函數(partially applied function),如:

def sum(a:Int,b:Int,c:Int) = a+b+c

val p0 = sum _ // 正確

val p1 = sum(10,_,20) // 錯誤

val p2 = sum(10,_:Int,20) // 正確

val p3 = sum(_:Int,100,_:Int)

p0(1,2,3) // 6

p2(100) // 130

p3(10,1) // 111

或者:

  (sum _)(1 2 3) // 6

  (sum(1,_:Int,3))(2) // 6

  (sum(_:Int,2,_:Int))(1,3) // 6

從上面能夠看出,partial函數是一個正常函數中抽出來的一部分(參數不全),故稱爲partial函數。

3.1.8.  「_」匿名參數

_:匿名函數中的匿名參數,同Groovyit同樣,groovy 3.times { print it };但也有差異:

 

Groovy

Scala

print({it*10}(5)) // 50

((_:Int)*10)(5) // 50

強類型語言,必須標明類型爲Int

不一樣

def a = {it*it}(5) // 25

 

兩個it徹底同樣

((_:Int)*(_:Int))(5,5) // 25

((it:Int)=>it*it)(5) // 25

不能用:((_:Int)*(_:Int))(5)

兩個_不同,表示2個不一樣參數

注意:多個下劃線指代多個參數,而不是單個參數的重複使用。第一個下劃線表明第一個參數,第二個下劃線表明第二個,第三個……,如此類推。

 

Java

int add(int i, int j) { return i+j; }

add(3,4)

返回值、函數名和return關鍵字能夠省略,變爲:

 

scala 寫法1

((i:Int, j:Int) => i+j)(3, 4) // 7

i,j變量名能夠省略,變爲:

 

scala寫法2

    ((_:Int) + (_:Int))(3,4) // 7

    注意:括號(_:Int)括號是必須的

 

其餘例子

"Hello".exists(_.isUpper) // true

例子2

def sum(x:Int,y:Int,z:Int) = x+y+z

val sum1 = sum _

val sum2 = sum(1000,_:Int,100)

sum1(1,2,3) // 6

sum2(5) // 1105

 

若是_在最後,還能夠省略

1 to 5 foreach println

1 to 5 map (10*)

1 to 5 map (*10) // 錯誤!_不在最後不能省

3.1.9.  變長參數 *

變長參數只能放在最後一個,不然就歧義了,

def sum(ns:Int*, s:String) = ... // 錯誤

例子1

def sum(ns: Int*) = {

    var s = 0

    for (n<-ns) s += n

    s

}

sum(1,2,3,4) // 10

更函數化的寫法:

def sum(ns:Int*) = ns.reduceLeft(_+_)

sum(1,2,3,4) // 10

 

例子2

def m(args:Any*) = args foreach println

scala> m(3,3.14,"hello",List(1,2,3))

3

3.14

hello

List(1, 2, 3)

 

3.1.10.      lazy參數

就是調用時用到函數的該參數,每次都從新計算:

例子1

通常參數

lazy參數

def f1(x: Long) = {

  val (a,b) = (x,x)

  println("a="+a+",b="+b)

}

def f2(x: =>Long) = {

  val (a,b) = (x,x)

  println("a="+a+",b="+b)

}

f1(System.nanoTime)

// a=140857176658355,b=140857176658355

f2(System.nanoTime)

// a=140880226786534,b=140880226787666

例子2 lazy參數是函數

通常參數

lazy參數

def times3(m: Unit) = { m;m;m }

def times3(m: =>Unit) = { m;m;m }

times3 ( println("hello world") )

// 打印1

times3 ( println("hello world") )

// 打印3

例子3

通常參數

lazy參數

def f(x:Int m: Unit) = { print(x*x); m }

def f(x:Int, m: =>Unit) = { print(x*x); m }

f(3,println("end"))

// 先執行m,輸出:"end\n9"

f(3,println("end"))

// lazy執行m,輸出 9end

 

例子4:自定義loop

def loop2 (cond: =>Boolean)(body: =>Unit): Unit =

  if (cond) { body; loop2(cond)(body) }

調用:

var i=0; loop2 (i<3) (i+=1) // i=3

或者

var i=0; loop2 {i<3} {i+=1} // i=3

或者咱們更習慣的方式:

var i=0; loop2 (i<3) {i+=1}

 

例子5:自定義loop..until

def loop3 (body: =>Unit): foo = new foo(body)

class foo(body: =>Unit) {

  def until(cond: =>Boolean) { body; if(!cond) until(cond) }

}

//調用:

var i = 0

loop3 { i+=1; println(i) } until (i>3)

 

3.2.     函數式編程

3.2.1.  風格

首選使用val,用var以前要仔細考慮是否真正須要。

非函數式

函數式

var s = ""

if (args.length>0) s = args[0]

val s = if (args.length>0) args[0] else ""

def gcd(a0:Int,b0:Int) = {

  var a = a0; var b = b0

  while(a!=0) {

val t = a

a = a % b

b = t

  }

  return b

}

ab的最大公約數,全部的while循環基本均可以被替代:

def gcd(a:Int,b:Int) =

if (b==0) a else gcd(b, a % b)

 

函數要短,能夠試着全部的函數體都沒有 {...} 包圍,一旦出現,儘可能分解

 

例如打印

1  2  3

2  4  6

3  6  9

非函數式

函數式

for(i<-1 to 3; j<-1 to 3) {

print(i*j + " ");

if (j==3) println

}

def f(n:Int) = 1 to 3 map (_*n) mkString " "

1 to 3 map f mkString "\n"

 

3.2.2.  函數做爲參數

採用映射式函數定義:

例子1

def f(x:Int, y:Int, m:(Int, Int)=>Int) = m(x,y)

f(3,4, (x,y)=>x+y) // 7

f(3,4, (x,y)=>scala.math.sqrt(x*x+y*y)) // 5

例子2

def f(x:Int, y:Int, m: =>Unit) = { println(x*y); m } // 參見「lazy參數」

f(3,4, println("end"))

// 12

// end

例子3

  def f(f2:Int=>Int) = f2(5)

  f(100+) // 105, 100+是匿名函數 ((x:Int)=>100+x)的簡寫

3.2.3.  函數做爲返回值

def f(s:String):(Int => String) = { n:Int=> s * n }

f("*") // res32: (Int) => String = <function1>

res32(10) // "**********"

f("*")(10) // 至關於先獲得f("*")的返回值函數,再用該返回值函數調用一個參數

3.2.4.  Curry

例如:

def sum(a:Int, b:Int) = { a + b } // sum(1, 2) = 3

Curry化後:

def sum(a:Int)(b:Int) = { a + b } // sum(1)(2) = 3

或者:

def sum(a:Int) = { (b:Int)=> a + b } // sum(1)(2) = 3

// 調用方式二:val t1 = sum(10);  val t2 = t1(20)

 

3.2.5.  遞歸

使用遞歸的緣由:

其一是不少數學關係、邏輯關係自己就是遞歸描述(HanoiFib)的;

其二是函數式編程不鼓勵用變量循環,而是用遞歸。

遞歸包含遞歸出口和遞歸體:

遞歸出口

即遞歸的終止條件

好比0!=1, fib(1)=fib(2)=1, index==1000

遞歸體

即和前面或後面的值之間的關係

好比n! = n*(n-1)!, fib(n)=fib(n-2)+fib(n-1)

 

例子1:冪運算

如計算2^100, scala.math.pow(2,2000)越界,可用遞歸

def pow(n:BigInt, m:BigInt):BigInt = if (m==0) 1 else pow(n,m-1)*n

 

例子2Hanoi漢諾塔

a挪到c

a(from)

b(via)

c(to)

=     --- 1

===

  =====   --- n-1

=======  --- n

 

 

=======  --- n

=     --- 1

===

  =====   --- n-1

 

 

=     --- 1

===

  =====   --- n-1

=======  --- n

 

 

=     --- 1

===

  =====   --- n-1

=======  --- n

def move(n:Int, a:Int, b:Int, c:Int) = {

  if (n==1) println("%s to %s" format (a, c)) else {

move(n-1, a,c,b); move(1,a,b,c); move(n-1,b,a,c) }}

例子3:最大公約數

12  20

    20  12 (=12%20)

12  8 (=20%12)

        8  4 (=12%8)

           4  0 (=8%4)

def gcd(n:Int, m:Int):Int = if(m==0) n else gcd(m, n%m)

 

例子4:上臺階

N級臺階,便可每次1步也可每次2步走,共有多少種不一樣走法

解法:邁出第一步有兩種方法,第一步後就是N-1N-2的走法了

def step(n:Int):Int = if (n<=2) n else step(n-1)+step(n-2)

推廣:若是每次可走1步或2步或3步,則

def step(n:Int):Int = n match { case 1=>1; case 2=>2; case 3=>4;

case _ =>step(n-1)+step(n-2)+step(n-3) }

3.2.6.  -遞歸

定義:函數尾(最後一條語句)是遞歸調用的函數。

tail-recursive會被優化成循環,因此沒有堆棧溢出的問題。

 

線性遞歸的階乘:

def nn1(n:Int):BigInt = if (n==0) 1 else nn1(n-1)*n

println(nn1(1000)) // 4023...000

println(nn1(10000)) // 崩潰:(

 

尾遞歸的階乘:

def nn2(n:Int, rt:BigInt):BigInt = if (n==0) rt else nn2(n-1, rt*n)

println(nn2(1000,1)) // 40...8896

println(nn2(10000,1)) // 2846...000

 

def nn3(n:Int):BigInt = nn2(n,1)

 

對比:

線性遞歸

尾遞歸

nn1(5)  
{5 * nn1(4) }
{5 * {4 * nn1(3) }}
{5 * {4 * {3 * nn1(2) }}}
{5 * {4 * {3 * {2 * nn1(1) }}}}
{5 * {4 * {3 * {2 * 1}}}}
{5 * {4 * {3 * 2}}}
{5 * {4 * 6}}
{5 * 24}
120

nn2(5, 1)
nn2(4, 1*5=5)
nn2(3, 4*5=20)
nn2(2, 3*20=60)
nn2(1, 2*60=120)
120

不算,直到遞歸到一個肯定的值後,又從這個具體值向後計算;更耗資源,每次重複的調用都使得調用鏈條不斷加長系統使用棧進行數據保存和恢復

每遞歸一次就算出相應的結果。

不能優化成循環

能夠優化成循環

 

例子:查找第10001個質數:

def prime(n:Int) = 2 to math.sqrt(n).toInt forall (n%_!=0)

def next(p:Int) = p+1 to 2*p find prime get

def f(p:Int, i:Int):Int = if (i==10001) p else f(next(p), i+1)

f(2,1) // 104743

或者使用Stream

def f2(p:Int):Stream[Int] = p #:: f2(next(p))

f2(2).take(10001).last // 104743

 

例子:fib數列(1 1 2 3 5 8 13 21)

def fib2(n1:BigInt, n2:BigInt, i:Int, n:Int):BigInt =

  if (i==n) n1 else fib2(n2, n1+n2, i+1, n)

def fib(n:Int) = fib2(1,1,0,n)

0 to 10 map fib

fib(100-1) // 100fib=354224848179261915075

4.   OOP

4.1.     class

4.1.1.  定義

例子1

class User {

var name = "anonymous"

var age:Int = _

val country = "china"

def email = name + "@mail"

}

使用:

val u = new User

// var定義的屬性可讀可寫

u.name = "qh"; u.age = 30

println(u.name + ", " + u.age) // "qh, 30"

// val 定義的屬性只讀不可寫

u.country = "usa" // 報錯

println(u.country) // "china"

// def 定義的是方法每次調用時從新計算

u.email // "qh@mail"

 

例子2

// 定義

class Person(ln : String, fn : String, s : Person = null) {

  def lastName = ln;   // def定義後纔是屬性,lnfns不可見

  def firstName = fn;

  def spouse = s;

  def introduction() : String =

    return ("Hi, " + firstName + " " + lastName) +

    (if (spouse != null) " and spouse, " + spouse.firstName + " " + spouse.lastName + "."

     else ".");

}

// 調用

new Person("aa","bb", new Person("cc","dd")).introduction();

 

4.1.2.  構造方法

class c1(x:String) // 等同於:class c1(private var x:String)

val o1 = new c1("aaa")

o1.x // 報錯,由於是private的,定義成 class c1(var x:String) 才能這樣用

 

例子1

object construct1 {

class c1(name:String, age:Int) { // (1)直接在類定義處

    def this() { this("anonymous", 20) } // (2)this定義

        def m1() = {  printf("%s=%d\n", name, age) }

    }

    def main(args:Array[String]) = {

new c1().m1()

new c1("qh", 30).m1()

}

}

編譯:fsc construct1.scala

運行:java construct1

 

例子2:繼承中的構造方法:

class c2(name:String, age:Int, female:Boolean=false)

    extends c1(name,age) {

    override def toString = { name + "," +  age + "," + female }

}

 

4.1.3.  override

不一樣於Java的使用 @Override,或者直接使用相同名字覆蓋父類方法。

    override def toString = { name + "," +  age + "," + female }

若是是覆蓋抽象方法,能夠不用overriade關鍵字。

4.1.4.  object單例對象

如:

Java

Scala

public class User {

  private String name;

  private User(String name) { this.name=name; }

  public static User instance(String name) {

return new User(name)

  }

}

object User {    

  var name:String = _

  def apply(name:String){this.name=name; this}

  override def toString = "name: " + name

}

調用:

val u = User("qh") // "name: qh"

 

 

4.1.5.  靜態方法

Scala沒有靜態方法,相似靜態方法的函數定義在object中:

object Stringx {

    def left(s0:String, s:String) = ...

}

直接調用Stringx.left(s0, s),或者 Stringx left (s0, s)

 

定義在object中的implicit方法也能被直接調用:

例如:

--------- ImportSub.scala

object ImportSub {

  def fac(n: Int) = 1 to n reduceLeft (_ * _)

  implicit def foo(n: Int) = new { def ! = fac(n) }

}

--------- ImportMain.scala

import ImportSub._

object ImportMain {

  def main(args : Array[String]) : Unit = {

      println(5!// 調用ImportSub中定義的implicit函數

  }

}

4.1.6.  case class(條件類)

例如:

case class Person(name:String, age:Int)   

特殊之處:

l  新建類實例不用new Person(..),直接用Person("qh",20)

l  自動定義好getXX方法,Person("qh",20).name // "qh"

l  提供默認的toString(), Person("qh",20) // "Person(qh,20)"

l  結合類繼承能夠經過模式匹配進行分解

例子1

abstract class Person

case class Man(power:Int) extends Person

case class Woman(beauty:Int, from:String) extends Person

 

val w1 = Woman(100,"china")

val w2 = w1.copy(from="usa") // Woman(100,"usa")

 

def f(t:Person) = t match {

case Man(x) => "man's power:" + x

case Woman(x,y) => y + " beauty:" + x

}

f(Man(100)) // man's power:100

f(Woman(90, "china")) // china beauty:90

 

注:基本類型直接能夠用math case

 

例子2:可變的類狀態

case class C1(var s: String, var ops: Int) { 

  def >>= (f: (String=>String)) = {

    s = f(s)  // s改變

    ops += 1  // ops改變

    this // 返回自身,能夠連續調用

  } 

}  

val C1(res, ops) = C1("ab", 0) >>= (_ * 3) >>= (_ drop 3)

// res="ab"->"ababab"->"bab", ops=0-> 0+1+1->2

 

例子3:用case class代替tuple

val p = ("qh",20) // p._1 = "qh", p._2 = 20;好處是簡潔,但無心義

case class person(name:String, age:Int)

val p = person("qh",20) // p.name = "qh", p.age = 20; 好處是有名字,自說明,可讀性強

 

例子4:用case class來描述元數據

xml的版本:

<todo name = "housework">

<item priority = "high">Clean the hose</item>

<item priority = "medium">Wash the dishes</item>

<item priority = "medium">Buy more soap</item>

</todo>

Lisp的版本:

(todo "housework"

 (item (priority high) "Clean the house")

 (item (priority medium) "Wash the dishes")

 (item (priority medium) "Buy more soap"))

Scala的版本:

    case class item(priority:String, s:String)

    case class todo(name:String, items:List[item])

    todo (name="housework",

items=item("high","Clean the house")::

item("medium","Wash the dishes")::

item("medium","Buy more soap")::Nil)

 

4.1.7.  case object(條件單例對象)

好比定義一個標識類(而不是字符串):

case object Start

case object Stop

 

4.1.8.  枚舉

Java中:

   enum fruits { apple, banana, cherry }

Scala中,則是:

sealed abstract class Fruits // sealed相似於javafinal

case object Apple extends Fruits

case object Banana extends Fruits

case object Cherry extends Fruits

也能夠是 case class

 

4.1.9.  屬性和Bean

例子1(直接定義和使用屬性):

class c {

var name = "anonymous" // var定義的是r/w的屬性

val age = 20  // val定義的是隻r屬性

}

 

val o = new c

o.name = "qh"

o.name // "qh"

o.age = 10 // 錯誤

o.age // 20

o.

 

例子2(定義get/set方法):

class c2 {

@reflect.BeanProperty var name = "anonymous"

}

val o2 = new c2

o2.name = "qh" // 也能夠直接存取

o2.name // "qh"

o2.setName("james") // 增長了set/get方法

o2.getName() // "james"

4.1.10.      反射

Scala沒有太特別的反射機制,使用java的便可,不過Scalamatch..case中能夠匹配類型:

    case o:FooClass1 => ...

相關還有isInstanceOf[T], asInstanceOf[T]

 

1(利用javareflect):

"hello".getClass.getMethods.map(_.getName).toList.sortWith(_<_).mkString(", ")

 

例子2

classOf[String] // 至關於java中的String.class

"aaa".isInstanceOf[String] // true

"aaa".asInstanceOf[String]

4.2.     trait超級接口

注:trait  [treit] n.特徵,特色,特性

JavaInterface相似,但能夠定義實體方法,而非僅僅方法定義

trait能夠看做有方法實現和字段的interface;表明一類事物的特性;

好比

Tom,多是Engine Son兩個trait的混合;

Sunny可能SalesSonFather三個trait的混合;

當在運行時往Son裏面增長方法或者字段的時候,TomSunny都獲得增長的特性。

4.2.1.  trait使用

trait Runnable {

  def run(): Unit;

}

 

只是用一個接口,就用extends:

class c1 extends Runnable {...}

 

2個接口(或一個繼承一個接口),用with而不是implements以下:

class c1 extends c0 with Runnable {

    def run(): Unit = {...}

}

 

一個類能夠組合多個trait

class c1 extends t1 with t2 with t3 {...}

 

4.2.2.  mixin

class Human

class Child

 

trait Dad {

    private var children:List[Child] = Nil

    def add(child:Child) = child :: children

}

 

class Man1(name:String) extends Human with Dad // 靜態mixin

class Man2(name:String) extends Human // 先不具有Dad trait

 

val m1 = new Man1("qh")

m1.add(new Child)

     

val m2 = new Man2("小孩")

//    m2.add(new Child) // 報錯   

val m2$ = new Man2("james"with Dad // 動態mixin

m2$.add(new Child)

 

 

4.3.     協變和逆變(co-|contra-)variance

4.3.1.  概念

使用「+」「-」差別標記

Function[A, B]Function[-A, +B]的區別圖示:

Function[A,B]

Function[-A,+B]

 

trait Queue[T] {}

非變

trait Queue[+T] {}

協變

若是S extends A (S爲子類型,A爲父類型)

Queue[S]爲子類型,Queue[A]爲父類型

S <: A => Queue[S] <: Queue[A]            

trait Queue[-T] {}

逆變

若是S extends A (S爲子類型,A爲父類型)

Queue[S]爲父類型,Queue[A]爲子類型,和協變互逆

S <: A => Queue[S] >: Queue[A]

 

-AA的子集,叫逆變

+BB的超集,叫協變

 

4.3.2.  類型上下界

 

 

<%

foo[T <% Ordered[T]](...)

關係較弱:T可以隱式轉換爲Ordered[T]

<:

foo[T <: Ordered[T]](...)

關係較強:T必須是Ordered[T]的子類型,即T的類型範圍小於Ordered[T]Ordered[T]爲上界

>:

foo[T >: A](...)

關係較強:T必須是A的父類型,即Tde類型範圍大於AA爲下界

 

4.3.3.  協變、逆變結合上下界

例子1

trait c1[+T] {

def m[K >: T](x:K) = x }

trait c1[-T] {

def m[K <: T](x:K) = x }

object c2 extends c1[Int]

c2.m(3) // 3

c2.m(3.0) // 3.0

c2.m("abc") // "abc"

object c2 extends c1[Int]

c2.m(3) // 3

c2.m(3.0) // 報錯

c2.m("abc") // 報錯

 

 

 

例子2

// 非變

case class T1[T](e:T)

val v1:T1[java.lang.Integer] = new T1(100)

val v2:T1[java.lang.Integer] = v1

v2.e // 100

val v3:T1[java.lang.Number] = v1 // 報錯

 

// 協變

case class T1[+T](e:T)

val v1:T1[java.lang.Integer] = new T1(100)

val v2:T1[java.lang.Integer] = v1

v2.e // 100

val v3:T1[java.lang.Number] = v1 // 合法

v3.e // 100

val v4:T1[java.lang.Integer] = v3 //非法

 

// 逆變

class T1[-T](e:T)

val v1:T1[java.lang.Number] = new T1(100)

val v2:T1[java.lang.Number] = v1

val v3:T1[java.lang.Integer] = v1 // 合法

val v4:T1[java.lang.Number] = v3 // 非法

5.   util

5.1.     架構

http://www.scala-lang.org/docu/files/collections-api/collections.html

 

scala.collection.immutable

 

scala.collection.mutable

 

不可變(collection.immutable._

可變(collection.mutable._

Array

ArrayBuffer

List

ListBuffer

String

StringBuilder

/

LinkedList, DoubleLinkedList

List

MutableList

/

Queue

Array

ArraySeq

Stack

Stack

HashMap HashSet

HashMap HashSet

 

ArrayStack

 

5.2.     集合Array,List,Tuple

Array

長度固定

元素可變

肯定長度,後賦值;

List

長度固定

元素不可變

 

Tuple

長度固定

元素不可變

經常使用於有多個返回值的函數;或者多個變量的同時定義

Scala 2.8中,3者的元素均可以混合不一樣的類型(轉化爲Any類型);

Scala 2.7中,ArrayList都不能混合類型,只有Tuple能夠;

5.2.1.  定義和初始化

5.2.1.1     Array

val list1 = new Array[String](0) // Array()

val list2 = new Array[String](3) // Array(null, null, null)

val list3:Array[String] = new Array(3) // // Array(null, null, null)

val list1 = Array("a","b","c","d") // 至關於Array.apply("a","b","c","d")

定義一個類型爲AnyArray

val aa = Array[Any](1, 2)

或:

val aa: Array[Any] = Array(1, 2)

或:

val aa: Array[_] = Array(1, 2)

 

定義:

Array (1,3,5,7,9,11)

也能夠用

Array[Int](1 to 11 by 2:_*)

 

Array對應的可變ArrayBuffer

val ab = collection.mutable.ArrayBuffer[Int]()

ab += (1,3,5,7)

ab ++= List(9,11) // ArrayBuffer(1, 3, 5, 7, 9, 11)

ab toArray // Array (1, 3, 5, 7, 9, 11)

ab clear // ArrayBuffer()

5.2.1.2     List

val list:List[Int] = List(1,3,4,5,6) // 或者 List(1 to 6:_*)

val list1 = List("a","b","c","d") // 或者 List('a' to 'd':_*) map (_.toString)

元素合併進List::

val list2 = "a"::"b"::"c"::Nil // Nil是必須的

val list3 = "begin" :: list2 // list2不變,只能加在頭,不能加在尾

多個List合併用++,也能夠用:::(不如++)

val list4 = list2 ++ "end" ++ Nil

val list4 = list2 ::: "end" :: Nil // 至關於 list2 ::: List("end")

 

 import java.util._ 以後會產生衝突,須要指明包

scala.List(1,2,3)

 

ListBuffer是可變的:

val lb = scala.collection.mutable.ListBuffer(1,2,3)

lb.append(4) // ListBuffer(1, 2, 3, 4)

 

建議定義方式:

val head::body = List(4,"a","b","c","d")

// head: Any = 4

// body: List[Any] = List(a, b, c, d)

val a::b::c = List(1,2,3)

// a: Int = 1

// b: Int = 2

// c: List[Int] = List(3)

 

定義固定長度的List

List.fill(10)(2) // List(2, 2, 2, 2, 2, 2, 2, 2, 2, 2)

Array.fill(10)(2) // Array(2, 2, 2, 2, 2, 2, 2, 2, 2, 2)

又如:

List.fill(10)(scala.util.Random.nextPrintableChar)

// List(?, =, ^, L, p, <, \, 4, 0, !)

List.fill(10)(scala.util.Random.nextInt(101))

// List(80, 45, 26, 75, 24, 72, 96, 88, 86, 15)

 

List對應的可變ListBuffer

val lb = collection.mutable.ListBuffer[Int]()

lb += (1,3,5,7)

lb ++= List(9,11) // ListBuffer(1, 3, 5, 7, 9, 11)

lb.toList // List(1, 3, 5, 7, 9, 11)

lb.clear // ListBuffer()

 

5.2.1.3     Vector

Scala2.8爲了提升list的隨機存取效率而引入的新集合類型(而list存取前部的元素快,越日後越慢)。

val v = Vector.empty

val v2 = 0 +: v :+ 10 :+ 20 // Vector(0, 10, 20), Vector 那一邊始終有":"

v2(1) // 10

v2 updated (1,100) // Vector(0, 100, 20)

Seq的缺省實現是List

Seq(1,2,3) // List(1, 2, 3)

IndexSeq的缺省實現是Vector:

IndexSeq(1,2,3) // Vector(1, 2, 3)

 

5.2.1.4     Tuple

val t1 = ("a","b","c")

var t2 = ("a", 123, 3.14, new Date())

val (a,b,c) = (2,4,6)

最簡單的Tuple

1->"hello world"

和下面的寫法是等價的:

(1, "hello world")

 

5.2.1.5     Range

Range(0, 5) // (0,1,2,3,4)

等同於:

0 until 5

等同於:

0 to 4

 

兩個Range相加:

('0' to '9') ++ ('A' to 'Z') // (0,1,..,9,A,B,...,Z)

 

Range和序列轉換:

1 to 5 toList

至關與:

List(1 to 5:_*)

或者:

Vector(1 to 5: _*) // Vector(1,2,3,4,5)

5.2.1.6     Stream

Stream至關於lazy List,避免在中間過程當中生成沒必要要的集合。

定義生成:

val st = 1 #:: 2 #:: 3 #:: Stream.empty // Stream(1, ?)

 

例子:fib數列的Stream版本簡單易懂

def fib(a: Int, b: Int): Stream[Int] = a #:: fib(b,  a+b)

val fibs = fib(1, 1).take(7).toList // List(1, 1, 2, 3, 5, 8, 13)

fib數列的先後項比值趨於黃金分割:

def fn(n:Int) = fib(1,1)(n)

1 to 10 map (n=> 1.0*fn(n)/fn(n+1)) // Vector(0.5, 0.666, ..., 0.618)

 

例子1

Range        (1,50000000).filter (_ % 13==0)(1) // 26, 但很慢,須要大量內存

Stream.range(1,50000000).filter(_%13==0)(1) // 26,很快,只計算最終結果須要的內容

注意:

第一個版本在filter後生成一箇中間collectionsize=50000000/13;然後者不生成此中間collection,只計算到26便可。

 

例子2

(1 to 100).map(i=> i*3+7).filter(i=> (i%10)==0).sum // mapfilter生成兩個中間collection

(1 to 100).toStream.map(i=> i*3+7).filter(i=> (i%10)==0).sum

 

5.2.1.7     Stack Queue

先進後出的堆棧:

val s = collection.immutable.Stack()

val s2 = s.push(10,20,30) // Stack(30, 20, 10)

s2.head // 30

s2.pop.pop // Stack(10)

對應的可變Stack

val ms = collection.mutable.Stack()

ms.push(1,3,5).push(7) // Stack(7, 5, 3, 1)

ms.head // 7

ms.pop // 7, ms = Stack(5,3,1)

 

先進先出的隊列:

val q = collection.immutable.Queue() // 也可指定類型 Queue[Int]()

val q2 = q.enqueue(0).enqueue(List(10,20,30)) // Queue(0, 10, 20, 30)

q2.dequeue._1 // 0

q2.dequeue._2 // Queue(10, 20, 30)

對應的可變Queue

val mq = collection.mutable.Queue[Int]()

mq += (1,3,5)

mq ++= List(7,9) // Queue(1, 3, 5, 7, 9)

mq dequeue // 1, mq= Queue(3, 5, 7, 9)

mq clear // Queue()

5.2.2.  使用(map, flatMap, filter, exists)

5.2.2.1     map

// 類型能夠混合:

import java.util._

val list3 = Array("a", 123, 3.14, new Date())

List("a","b","c").map(s=> s.toUpperCase()) // 方式1

List("a","b","c").map(_.toUpperCase())     // 方式2, 相似於Groovyit

// = List(A, B, C)

 

5.2.2.2     filter filterNot

List(1,2,3,4,5).filter(_%2==0) // List(2, 4)

也能夠寫成

for (x<-List(1,2,3,4,5) if x%2==0) yield x

 

List(1,2,3,4,5).filterNot(_%2==0) // List(1, 3, 5)

 

5.2.2.3     partition span splitAt groupBy

注:val (a,b) = List(1,2,3,4,5).partition(_%2==0) // (List(2,4), List(1,3,5))

可把Collection分紅:知足條件的一組,其餘的另外一組。

partition類似的是span,但有不一樣:

List(1,9,2,4,5).span(_<3)       // (List(1),List(9, 2, 4, 5)),碰到不符合就結束

List(1,9,2,4,5).partition(_<3) // (List(1, 2),List(9, 4, 5)),掃描全部

 

List(1,3,5,7,9) splitAt 2 // (List(1, 3),List(5, 7, 9))

List(1,3,5,7,9) groupBy (5<) // Map((true,List(7, 9)), (false,List(1, 3, 5)))

5.2.2.4     foreach

打印

Array("a","b","c","d").foreach(printf("[%s].",_))

// [a].[b].[c].[d].

 

5.2.2.5     exists

// 集合中是否存在符合條件的元素

List(1,2,3,4,5).exists(_%3==0) // true

 

5.2.2.6     find

返回序列中符合條件的第一個。

例子:查找整數的第一個因子(最小因子、質數)

def fac1(n:Int) = if (n>= -1 && n<=1) n else (2 to n.abs) find (n%_==0) get

5.2.2.7     sorted sortWith sortBy

例子(排序):

List(1,3,2,0,5,9,7).sorted //  List(0, 1, 2, 3, 5, 7, 9)

List(1,3,2,0,5,9,7).sortWith(_>_) // List(9, 7, 5, 3, 2, 1, 0)

List("abc", "cb", "defe", "z").sortBy(_.size) // List(z, cb, abc, defe)

List((1,'c'), (1,'b'), (2,'a')) .sortBy(_._2) // List((2,a), (1,b), (1,c))

 

5.2.2.8     distinct

例子:(去除List中的重複元素)

def uniq[T](l:List[T]) = l.distinct

uniq(List(1,2,3,2,1)) // List(1,2,3)

 

5.2.2.9     flatMap

flatMap的做用:把多層次的數據結構「平面化」,並去除空元素(如None)。

可用於:獲得xml等樹形結構的全部節點名稱,去除None

 

例子1a:(兩個List作乘法)

List(1,2,3) * List(10,20,30) = List(10, 20, 3020, 40, 6030, 60, 90)

val (a,b) = (List(1,2,3), List(10,20,30))

flatMap (i=> b map (j=> i*j))

等同於:

for (i<-a; i<-b) yield i*j // 這個寫法更清晰

例子1b

若是不用flatMap而是用map,結果就是:

map (i=> b map (j=> i*j)) // List(List(10, 20, 30), List(20, 40, 60), List(30, 60, 90))

等同於:

for (i<-a) yield { for (j<-b) yield i*j } // 不如上面的清晰

 

例子2

List("abc","def") flatMap (_.toList) // List(a, b, c, d, e, f)

List("abc","def") map (_.toList) // List(List(a, b, c), List(d, e, f))

 

例子3flatMap結合Option

def toint(s:String) =

try { Some(Integer.parseInt(s)) } catch { case e:Exception => None }

List("123", "12a", "45") flatMap toint // List(123, 45)

List("123", "12a", "45") map toint // List(Some(123), None, Some(45))

5.2.2.10   indiceszipWithIndex

獲得indices

val a = List(100,200,300)

indices // (0,1,2)

zipWithIndex // ((100,0), (200,1), (300,2))

(a indices) zip a // ((0,100), (1,200), (2,300))

 

截取一部分,至關於Stringsubstring

List(100,200,300,400,500) slice (2,4) // (300,400), l(2), l(3)

 

5.2.2.11   take drop splitAt

List(1,3,5,7) take 2 // List(1,3)

List(1,3,5,7) drop 2 // List(5,7)

5.2.2.12   count

知足條件的元素數目:

例如1000內質數的個數:

def prime(n:Int) = if (n<2) false else 2 to math.sqrt(n).toInt forall (n%_!=0)

1 to 1000 count prime  // 168

 

5.2.2.13   updated patch

對於immutable的數據結構,使用updated返回一個新的copy

val v1 = List(1,2,3,4)

v1.updated(3,10) // List(1, 2, 3, 10), v1仍是List(1, 2, 3, 4)

 

對於可變的數據結構,直接更改:

val mseq = scala.collection.mutable.ArraySeq(1, 2, 4, 6)

mseq(3) = 10 // mseq = ArraySeq(1, 2, 4, 10)

 

批量替換,返回新的copy

val v1 = List(1,2,3,4,5)

val v2 = List(10,20,30)

v1 patch (0, v2, 3) // List(10,20,30,4,5), v1,v2不變

5.2.2.14   reverse reverseMap

1 to 5 reverse // Range(5, 4, 3, 2, 1)

"james".reverse.reverse // "james"

reverseMap就是revese + map

  1 to 5 reverseMap (10*) // Vector(50, 40, 30, 20, 10)

至關於:

  (1 to 5 reverse) map (10*)

 

5.2.2.15   contains startsWith endWith

  1 to 5 contains 3 // true, 後一個參數是1個元素

  1 to 5 containsSlice (2 to 4) // true, 後一個參數是1個集合

(1 to 5) startsWith (1 to 3) // true 後一個參數是1個集合

(1 to 5) endsWith (4 to 5)

(List(1,2,3) corresponds List(4,5,6)) (_<_// true,長度相同且每一個對應項符合判斷條件

 

5.2.2.16   集合運算

List(1,2,3,4) intersect List(4,3,6) // 交集 = List(3, 4)

List(1,2,3,4) diff List(4,3,6) // A-B = List(1, 2)

List(1,2,3,4) union List(4,3,6) // A+B = List(1, 2, 3, 4, 4, 3, 6)

// 至關於

List(1,2,3,4) ++ List(4,3,6) // A+B = List(1, 2, 3, 4, 4, 3, 6)

 

5.2.2.17   異曲同工

例子:獲得 (4, 16, 36, 64, 100)

寫法1

(1 to 10) filter (_%2==0) map (x=>x*x)

寫法2

for(x<-1 to 10 if x%2==0) yield x*x

寫法3

(1 to 10) collect { case x if x%2==0 => x*x }

 

5.2.2.18   其餘

對其餘語言去重感興趣,可看看:

http://rosettacode.org/wiki/Remove_duplicate_elements 

 

5.2.3.  數組元素定位

統一使用(),而不是[]()就是apply()的簡寫,a(i)===a.apply(i)

// Array

val a = Array(100,200,300) // a(0)=100, a(1)=200, a(3)=300

a(0) // 100, 至關於a.apply(0)

a(0)=10 // Array(10, 200, 300),至關於a.update(0, 10)

 

// List

val list = List("a","b","c")

// list(0)=="a", list(1)=="b", list(2)=="c"

 

// Tuple

val t1 = ("a","b","c") // t1._1="a", t1._2="b", t1._3="c"

 

5.2.4.  view

在某類型的集合對象上調用view方法,獲得相同類型的集合,但全部的transform函數都是lazy的,從該view返回調用force方法。

對比:

val v = Vector(1 to 10:_*)

v map (1+) map (2*) // Vector(4, 6, 8, 10, 12, 14, 16, 18, 20, 22)

以上過程得生成2個新的Vector,而:

val v = Vector(1 to 10:_*)

v.view map (1+) map (2*) force

只在過程當中生成1個新的Vector,至關於:

v map (x=>2*(1+x))

 

又如:

((1 to 1000000000) view).take(3).force // Vector(1,2,3)

使用Stream

Stream.range(1,1000000000).take(3).force //  Stream(1, 2, 3)

5.2.5.  Java集合間的轉換(scalaj

方案一:JavaList<T>很容易經過List.toArray轉換到Array,和Scala中的Array是等價的,可以使用mapfilter等。

方案二:使用第三方的scalaj擴展包(需自行下載設置classpath

 

例子1

val a1 = new java.util.ArrayList[Int]

a1.add(100); a1.add(200); a1.add(300)

// 自行轉換

val a2 = a1.toArray

a2 map (e=>e.asInstanceOf[Int]) map(2*) filter (300>)

//採用scalaj(http://github.com/scalaj/scalaj-collection)

import scalaj.collection.Imports._

val a3 = a1.asScala

// scala->java

List(1, 2, 3).asJava

Map(1 -> "a", 2 -> "b", 3 -> "c").asJava

Set(1, 2, 3).asJava

// scalaj還能夠在javacollection上使用foreach (目前除foreach還不支持filtermap)

a1.foreach(println)

 

scalaj的簡易文檔以下:

// Java to Scala

Java類型
轉換方法
java.lang.Comparable[A] 
#asScala: scala.math.Ordered[A]
java.util.Comparator[A] 
#asScala: scala.math.Ordering[A]
java.util.Enumeration[A]
#asScala: scala.collection.Iterator[A]
#foreach(A => Unit): Unit
java.util.Iterator[A] 
#asScala: scala.collection.Iterator[A]
#foreach(A => Unit): Unit
java.lang.Iterable[A] 
 
#asScala: scala.collection.Iterable[A]
#foreach(A => Unit): Unit
java.util.List[A]
 
#asScala: scala.collection.Seq[A]
#asScalaMutable: scala.collection.mutable.Seq[A]
java.util.Set[A] 
 
#asScala: scala.collection.Set[A]
#asScalaMutable: scala.collection.mutable.Set[A]
java.util.Map[A, B] 
 
 
#asScala: scala.collection.Map[A, B]
#asScalaMutable: scala.collection.mutable.Map[A, B]
#foreach(((A, B)) => Unit): Unit
java.util.Dictionary[A, B] 
 
#asScala: scala.collection.mutable.Map[A, B]
#foreach(((A, B)) => Unit): Unit
// Scala to Java
Scala類型
轉換方法
scala.math.Ordered[A]                
#asJava: java.util.Comparable[A]
scala.math.Ordering[A]               
#asJava: java.util.Comparator[A]
scala.collection.Iterator[A]        
 
#asJava: java.util.Iterator[A]
#asJavaEnumeration: java.util.Enumeration[A]
scala.collection.Iterable[A]      
#asJava: java.lang.Iterable[A]
scala.collection.Seq[A]             
#asJava: java.util.List[A]
scala.collection.mutable.Seq[A]    
#asJava: java.util.List[A]
scala.collection.mutable.Buffer[A]
#asJava: java.util.List[A]
scala.collection.Set[A]             
#asJava: java.util.Set[A]
scala.collection.mutable.Set[A]    
#asJava: java.util.Set[A]
scala.collection.Map[A, B]        
#asJava: java.util.Map[A, B]
scala.collection.mutable.Map[A, B]
#asJava: java.util.Map[A, B]
#asJavaDictionary: java.util.Dictionary[A, B]

 

5.3.     Map

5.3.1.  定義Map

var m = Map[Int, Int]()

var m = Map(1->100, 2->200)

或者

var m = Map((1,100), (2,200))

相加:

val m = Map(1->100, 2->200) ++ Map(3->300)  // Map((1,100), (2,200), (3,300))

 

能夠用zip()生成Map

List(1,2,3).zip(List(100,200,300)).toMap // Map((1,100), (2,200), (3,300))

註解:zip有「拉拉鍊」的意思,就是把兩排鏈釦徹底對應扣合在一塊兒,很是形象。

5.3.2.  不可變Map(缺省)

l  定義:

val m2 = Map()

val m3 = Map(1->100, 2->200, 3->300) 

指定類型:

val m1:Map[Int,String] = Map(1->"a",2->"b")

注:若是import java.util._後發生衝突,可指明:scala.collection.immutable.Map

保持循序的Map可使用:

collection.immutable.ListMap

 

l  讀取元素:

// m3(1)=100, m3(2)=200, m3(3)=300

// m3.get(1)=Some(100), m3.get(3)=Some(300), m3.get(4)=None

val v = m3.get(4).getOrElse(-1) // -1

或者簡化成:

m3.getOrElse(4, -1) // -1

 

l  增長、刪除、更新:

Map自己不可改變,即便定義爲varupdate也是返回一個新的不可變Map

var m4 = Map(1->100)

val m5 = m4(1)=1000 // m4仍是 1->100, m51->1000

m4 += (2->200) // m4指向新的(1->100,2->200), (1->100)應該被回收

另外一種更新方式:

m4.updated(1,1000)

增長多個元素:

Map(1->100,2->200) + (3->300, 4->400) // Map((1,100), (2,200), (3,300), (4,400))

刪除元素:

  Map(1->100,2->200,3->300) - (2,3) // Map((1,100))

Map(1->100,2->200,3->300) -- List(2,3) // Map((1,100))

 

l  合併Mpa

Map(1->100,2->200) ++ Map(3->300) // Map((1,100), (2,200), (3,300))

 

5.3.3.  可變Map

val map = scala.collection.mutable.Map[String, Any]()

map("k1")=100     // 增長元素,方法1

map += "k2"->"v2" // 增長元素,方法2

// map("k2")=="v2", map.get("k2")==Some("v2"), map.get("k3")==None

有則取之,無則加之:

val mm = collection.mutable.Map(1->100,2->200,3->300)

mm getOrElseUpdate (3,-1) // 300, mm不變

mm getOrElseUpdate (4,-1) // 300, mm= Map((2,200), (4,-1), (1,100), (3,300))

刪除:

val mm = collection.mutable.Map(1->100,2->200,3->300)

mm -= 1 // Map((2,200), (3,300))

mm -= (2,3) // Map()

mm += (1->100,2->200,3->300) // Map((2,200), (1,100), (3,300))

mm --= List(1,2) // Map((3,300))

mm remove 1 // Some(300), mm=Map()

mm += (1->100,2->200,3->300)

mm.retain((x,y) => x>1) // mm = Map((2,200), (3,300))

mm.clearn // mm = Map()

改變value

mm transform ((x,y)=> 0) // mm = Map((2,0), (1,0), (3,0))

mm transform ((x,y)=> x*10) // Map((2,20), (1,10), (3,30))

mm transform ((x,y)=> y+3) // Map((2,23), (1,13), (3,33))

 

5.3.4.  JavaHashMap

使用JavaHashMap

val m1:java.util.Map[Int, String] = new java.util.HashMap

5.3.5.  讀取全部元素

上面說過,Map(1->100,2->200,3->300)  Map((1,100),(2,200),(3,300))的寫法是同樣的,可見Map中的每個entry都是一個Tuple,因此:

for(e<-map) println(e._1 + ": " + e._2)

或者

map.foreach(e=>println(e._1 + ": " + e._2))

或者(最好

for ((k,v)<-map) println(k + ": " + v)

 

也能夠進行filtermap操做:

map filter (e=>e._1>1) // Map((2,200), (3,300))

map filterKeys (_>1) // Map((2,200), (3,300))

map.map(e=>(e._1*10, e._2)) // Map(10->100,20->200,30->300)

map map (e=>e._2) // List(100, 200, 300)

至關於:

map.values.toList

 

按照key來取對應的value值:

2 to 100 flatMap map.get // (200,300) 只有key=23有值

5.3.6.  多值Map

結合MapTuple,很容易實現一個key對應的value是組合值的數據結構:

val m = Map(1->("james",20), 2->("qh",30), 3->("qiu", 40))

m(2)._1 // "qh"

m(2)._2 // 30

 

for( (k,(v1,v2)) <- m ) printf("%d: (%s,%d)\n", k, v1, v2)

5.4.     Set

注:BitSetcollection.immutable.BitSetSet相似,但操做更快

5.4.1.  定義

var s = Set(1,2,3,4,5) // scala.collection.immutable.Set

var s2 = Set[Int]() // scala.collection.immutable.Set[Int]

// 增長元素:

s2 += 1  // Set(1)

s2 += 3  // Set(1,3)

s2 += (2,4) // Set(1,3,2,4)

// 刪除元素

Set(1,2,3) - 2 // Set(1,3)

Set(1,2,3) - (1,2) // Set(3)

Set(1,2,3).empty // Set() 所有刪除

// 判斷是否包含某元素

s(3) // true, 集合中有元素3

s(0) // false, 集合中沒有元素0

// 合併

Set(1,2,3) ++ Set(2,3,4) // Set(1, 2, 3, 4)

Set(1,2,3) -- Set(2,3,4) // Set(1)

 

5.4.2.  邏輯運算

運算

例子

交集

Set(1,2,3) & Set(2,3,4) // Set(2,3)

Set(1,2,3) intersect Set(2,3,4)

並集

Set(1,2,3) | Set(2,3,4) // Set(1,2,3,4)

Set(1,2,3) union Set(2,3,4) // Set(1,2,3,4)

差集

Set(1,2,3) &~ Set(2,3,4) // Set(1)

Set(1,2,3) diff Set(2  ,3,4) // Set(1)

 

5.4.3.  可變BitSet

val bs = collection.mutable.BitSet()

bs += (1,3,5) // BitSet(1, 5, 3)

bs ++= List(7,9) // BitSet(1, 9, 7, 5, 3)

bs.clear // BitSet()

 

5.5.     Iterator

Iterator不屬於集合類型,只是逐個存取集合中元素的方法:

val it = Iterator(1,3,5,7) // Iterator[Int] = non-empty iterator

it foreach println // 1 3 5 7

it foreach println // 無輸出

 

三種經常使用的使用模式:

// 1、使用while

val it = Iterator(1,3,5,7)

while(it.hasNext) println(it.next)

// 2、使用for

for(e<- Iterator(1,3,5,7)) println(e)

// 3、使用foreach

Iterator(1,3,5,7) foreach println

 

Iterator也可使用map的方法:

Iterator(1,3,5,7) map (10*) toList // List(10, 30, 50, 70)

Iterator(1,3,5,7) dropWhile (5>) toList // List(5,7)

 

因爲Iterator用一次後就消失了,若是要用兩次,須要toList或者使用duplicate

val (a,b) = Iterator(1,3,5,7) duplicate // a = b = non-empty iterator

又如:

val it = Iterator(1,3,5,7)

val (a,b) = it duplicate

// 在使用ab前,不能使用it,不然ab都不可用了。

a toList // List(1,3,5,7)

b toList // List(1,3,5,7)

// 此時it也不可用了

5.6.     Paralllel collection

Scala 2.9+引入:

(1 to 10).par foreach println

多運行幾回,注意打印順序會有不一樣

6.   io

6.1.     文件I/O

6.1.1.  讀文件

scala特有的是scala.io.Source,例如:

import scala.io._

Source.fromFile("cn.scala","utf8").mkString

 

逐行讀文件內容:

Source.fromFile(new java.io.File("cn.scala")).getLines().foreach(println)

 

6.1.2.  寫文件

直接調用javaio

import java.io._, java.nio.channels._, java.nio._

// 寫文件

val f = new FileOutputStream("o.txt").getChannel

f write ByteBuffer.wrap("a little bit long ...".getBytes)

f close

 

或者:

var out = new java.io.FileWriter("./out.txt") // FileWriter("./out.txt", true爲追加模式

out.write("hello\n")

out close

 

6.1.3.  複製文件

直接調用javaio

val in  = new FileInputStream("in").getChannel

val out = new FileOutputStream("out").getChannel

in transferTo (0, in.size, out)

 

6.1.4.  全目錄掃描

遞歸使用listFiles

  import java.io.File

  class Dir(file:File) {

    // 目錄則返回全部1級子文件;文件則返回empty

def child = new Iterable[File] {

  // Iterable接口必須實現elements方法

      def elements = if (file.isDirectoryfile.listFiles.elements else Iterator.empty

    }   

    // 遞歸掃描,組成列表

    def scan:Iterable[File] = {

      Seq.singleton(file++ child.flatMap(i=> new Dir(i).scan)

    }

  }

  // 定義一個隱式類型轉換

  implicit def foo(f:File) = new Dir(f)

  val file = new File(".")

  Console println new File(".").getCanonicalPath

  val list = file.scan // File對象隱形轉換成Dir對象

  for (f<-listprintln(f)

 

6.2.     網絡I/O

import java.net.{URL, URLEncoder} import scala.io.Source.fromURL
fromURL(new URL("http://qh.appspot.com")).mkString

或者指定編碼:

fromURL(new URL("http://qh.appspot.com"))(io.Codec.UTF8).mkString

 

7.   actor

http://www.scala-lang.org/docu/files/actors-api/actors_api_guide.html#

 

Scala中處理併發,有不少選擇:

l  actor消息模型,相似Erlang,首選,Liftakka也實現了本身的actor模型。

l  ThreadRunnable

l  java.util.concurennt

l  3rd併發框架如NettyMina

7.1.     actor模型

Java內置線程模型

Scala actor模型

「共享數據-鎖」模型(share data and lock

share nothing

每一個object有一個monitor,監視多線程對共享數據的訪問

不共享數據,actor之間經過message通信

加鎖的代碼段用synchronized標識

 

死鎖問題

 

每一個線程內部是順序執行的

每一個actor內部是順序執行的

 

 

 

7.2.     多核計算

對好比下的算法:

def perfect(n:Int) =

n==(1 until n filter (n%_==0) sum)

val n = 33550336

// 串行計算

n to n+10 foreach (i=>println(perfect(i)))

def perfect(n:Int) =

n==(1 until n filter (n%_==0) sum)

val n = 33550336

// 並行計算

class T1(n:Int) extends Thread {

  override def run(){println(perfect(n))}}

n to n+10 foreach (i=>new T1(i).start)

耗時:8297

耗時:5134

單線程串行計算,不能很好發揮多核優點

多線程並行計算,平均分配到多核,更快

上面是Java的寫法,也能夠用Scalaactor寫法:

 

Scala寫法1

import actors.Actor,actors.Actor._

class A1 extends Actor {

def act { react { case n:Int=>println(perfect(n)) }}}

n to n+10 foreach (i=>{ (new A1).start ! i})

Scala寫法2

val aa = Array.fill(11)(actor { react { case n:Int=>println(perfect(n)) }})

n to n+10 foreach (i=>aa(i-n) ! i)

或者:

n to n+10 foreach (i=> actor { react { case n:Int=>println(perfect(n)) }} ! i)

 

7.3.     Actor用法

Scala會創建一個線程池共全部Actor來使用。receive模型是Actor從池中取一個線程一直使用;react模型是Actor從池中取一個線程用完給其餘Actor

 

實現方式1

import scala.actors._

object Actor1 extends Actor { // 或者class

// 實現線程

def act() { react { case _ =>println("ok"); exit} }

}

//發送消息:

Actor1.start ! 1001 // 必須調用start

 

實現方式2

import scala.actors.Actor._

val a2 = actor { react { case _ =>println("ok") } } // 立刻啓動

//發送消息:

a2 ! "message" // 沒必要調用start

 

提示:

! 

發送異步消息,沒有返回值。

!?

發送同步消息,等待返回值。(會阻塞發送消息語句所在的線程)

!!

發送異步消息,返回值是 Future[Any]

? 

不帶參數。查看 mailbox 中的下一條消息。

 

7.4.     方式1:接受receive

特色:要反覆處理消息,receive外層用while(..)

import actors.Actor, actors.Actor._

val a1 = Actor.actor {

var work = true

while(work) {

receive { // 接受消息或者用receiveWith(1000)

case msg:String => println("a1: " + msg)

case x:Int => work = false; println("a1 stop: " + x)

}

}

}

a1 ! "hello" // "a1: hello"

a1 ! "world" // "a1: world"

a1 ! -1 // "a1 stop: -1"

a1 ! "no response :("

7.5.     方式2:接受react, loop

特色:

l  從不返回

l  要反覆執行消息處理,react外層用loop不能while(..);

l  經過複用線程,比receive更高效,應儘量使用react

import actors.Actor, Actor._

val a1 = Actor.actor {

react {

        case x:Int => println("a1 stop: " + x)

        case msg:String => println("a1: " + msg); act()

    }

}

a1 ! "hello" // "a1: hello"

a1 ! "world" // "a1: world"

a1 ! -1 // "a1 stop: -1"

a1 ! "no response :("

 

若是不用退出的線程,可以使用loop改寫以下:

val a1 = Actor.actor {

  loop {

react {

case x:Int => println("a1 stop: " + x); exit()

        case msg:String => println("a1: " + msg)

    }

  }

}

 

7.6.     REPL接受消息

scala> self ! "hello"

scala> self.receive { case x => x }

scala> self.receiveWithin(1000) { case x => x }

 

7.7.     actor最佳實踐

7.7.1.  不阻塞actor

actor不該因爲處理某條消息而阻塞,能夠調用helper-actor處理耗時操做(helper actor雖然是阻塞的,但因爲不接受消息因此沒問題),以便actor接着處理下一條消息

-----------------------------------------

import actors._, actors.Actor._

 

val time = 1000

 

  // 1原來阻塞的程序

  val mainActor1 = actor {

    loop { react {

        case n: Int => Thread.sleep(time)

                         println(n)

        case s => println(s) } }

  }

  1 to 5 foreach { mainActor1 ! _ } // 5秒鐘後打印完數字

 

  // 2改寫由helper actor去阻塞的程序

  val mainActor2: Actor = actor {

    loop { react {

        case n: Int => actor { Thread.sleep(time); mainActor2 ! "wakeup" }

                        println(n)

        case s => println(s) } }

  }

  1 to 5 foreach { mainActor2 ! _ } // 立刻打印數字; 1秒鐘後打印5wakeup

-----------------------------------------

7.7.2.  actor之間用且僅用消息來通信

actor模型讓咱們寫多線程程序時只用關注各個獨立的單線程程序(actor),他們之間經過消息來通信。例如,若是BadActor中有一個GoodActor的引用:

class BadActor(a:GoodActor) extends Actor {...}

那在BadActor中便可以經過該引用來直接調用GoodActor的方法,也能夠經過「!」來傳遞消息。選擇後者!由於一旦BadActor經過引用讀取GoodActor實例的私有數據,而這些數據可能正被其餘線程改寫值,結果就避免不了「共享數據-鎖」模型中的麻煩事:即必須保證BadActor線程讀取GoodActor的私有數據時,GoodActor線程在這塊成爲「共享數據」的操做上加鎖。GoodActor只要有了共享數據,就必須來加鎖防範競用衝突和死鎖,你又得從actor模型退回到「共享數據-鎖」模型(注:actor對消息是順序處理的,原本不用考慮共享數據)。

7.7.3.  採用不可變消息

Scalaactor模型讓每一個actoract方法內部接近於單線程環境,你不用小心act方法裏面的操做是否線程安全。在act方法中你能夠盡情使用非同步、可變對象,由於每一個act方法被有效限制在單個線程中,這也是actor模型被稱爲「share-nothing 模型(零共享模型)的緣由,其數據的做用範圍被限制在單個線程中。不過一旦對象內的數據被用於多個actor之間進行消息傳遞。這時你就必須考慮消息對象是否線程安全。

保證消息對象線程安全的最好方法就是保證只使用不可變對象做爲消息對象。消息類中只定義val字段,且只能指向不可變對象。定義這種不可變消息類的簡單方法就是使用case class 並保證其全部的val字段都是不可變的。Scala API中提供了不少不可變對象可用,例如基本類型、StringTupleList,不可變Set、不可變Map等。

若是你發現確實須要把一個可變對象obj1發送給其餘actor,也因該是發送一份拷貝對象obj1.clone過去,而不是把obj1直接發過去。例如,數據對象Array是可變且未作同步的,因此Array只應該由一個actor同時存取,若是須要發送數組arr,就發送arr.clonearr中的元素也應該是不可變對象),或者直接發送一個不可變對象arr.toList更好。

總結:大部分時候使用不可變對象很方便,不可變對象是並行系統的曙光,它們是易使用、低風險的線程安全對象。當你未來要設計一個和並行相關的程序時,不管是否使用actor,都應該儘可能使用不可變的數據結構。

7.7.4.  讓消息自說明

對每一種消息建立一個對應的case class,而不是使用上面的tuple數據結構。雖然這種包裝在不少狀況下並不是必須,但該作法能使actor程序易於理解,例如:

// 不易理解,由於傳遞的是個通常的字符串,很難指出那個actor來響應這個消息

lookerUpper ! ("www.scala-lang.org", self)

// 改成以下,則指出只有react能處理LoopupIPactor來處理:

case class LookupIP(hostname: String, requester: Actor)

lookerUpper ! LookupIP("www.scala-lang.org", self)

 

7.8.     不一樣jvm間的消息訪問

服務器端:

object ActorServer extends Application {

    import actors.Actor, actors.Actor._, actors.remote.RemoteActor

    Actor.actor { // 建立並啓動一個 actor

      // 當前 actor 監聽的端口: 3000

      RemoteActor.alive(3000)

 

      //  3000 端口註冊本 actor,取名爲 server1

      // 第一個參數爲 actor 的標識,它以單引號開頭,是 Scala 中的 Symbol 量,

      // Symbol 量和字符串類似,但 Symbol 相等是基於字符串比較的。

      // self 指代當前 actor (注意此處不能用 this

      RemoteActor.register('server1Actor.self)

 

      // 收到消息後的響應

      loop {

        Actor.react {case msg =>

          println("server1 get: " + msg)

        }

      }

    }

}

 

客戶端:

  

object ActorClient extends Application { 

    import actors.Actor, actors.remote.Node, actors.remote.RemoteActor 

    Actor.actor { 

      // 取得一個節點(ip:port 惟一標識一個節點) 

      // Node 是個 case class,因此不須要 new 

      val node = Node("127.0.0.1", 3000) 

 

      // 取得節點對應的 actor 代理對象 

      val remoteActor = RemoteActor.select(node'server1

 

      // 如今 remoteActor 就和普通的 actor 同樣,能夠向它發送消息了! 

      println("-- begin to send message")

      remoteActor ! "ActorClient的消息" 

      println("-- end")

    } 

}

7.9.     STM

http://nbronson.github.com/scala-stm/

a lightweight Software Transactional Memory for Scala, inspired by the STMs in Haskell and Clojure.

Cheat-Sheet:

import scala.concurrent.stm._
 
val x = Ref(0) // allocate a Ref[Int]
val y = Ref.make[String]() // type-specific default
val z = x.single // Ref.View[Int]
 
atomic { implicit txn =>
  val i = x() // read
  y() = "x was " + i // write
  val eq = atomic { implicit txn => // nested atomic
    x() == z() // both Ref and Ref.View can be used inside atomic
  }
  assert(eq)
  y.set(y.get + ", long-form access")
}
 
// only Ref.View can be used outside atomic
println("y was '" + y.single() + "'")
println("z was " + z())
 
atomic { implicit txn =>
  y() = y() + ", first alternative"
  if (x getWith { _ > 0 }) // read via a function
retry // try alternatives or block 
} orAtomic { implicit txn =>
  y() = y() + ", second alternative"
}
 
val prev = z.swap(10) // atomic swap
val success = z.compareAndSet(10, 11) // atomic compare-and-set
z.transform { _ max 20 } // atomic transformation
val pre = y.single.getAndTransform { _.toUpperCase }
val post = y.single.transformAndGet { _.filterNot { _ == ' ' } }

 

 

8.   misc

8.1.     xml

8.1.1.  生成

Scala原生支持xml,就如同Java支持String同樣,這就讓生成xmlxhtml很簡單優雅:

val name = "james"

val age = 10

val html = <html>name={name}, age="{age}"</html> toString

// <html>name=james, age=&quot;10&quot;</html>

又如:

val html = <html><head><title>{myTitle}</title></head><body>{"hello world"}</boty></html>

 

更復雜的例子:

val x = <r>{(1 to 5).map(i => <e>{i}</e>)}</r>

// <r><e>1</e><e>2</e><e>3</e><e>4</e><e>5</e></r>

 

val x0 = <users><user name="qh"/></users>

val <users>{u}</users> = x0  // u: scala.xml.Node = <user name="qh"></user>

 

8.1.2.  xml文件

xml.XML loadString "<p></p>"

xml.XML loadFile "abc.xml"

 

xml.XML.saveFull("foo.xml", node, "UTF-8", 

xmlDecl: Boolean, doctype : DocType)

8.1.3.  讀取:

val x = <r>{(1 to 5).map(i => <e>{i}</e>)}</r>

// <r><e>1</e><e>2</e><e>3</e><e>4</e><e>5</e></r>

(x \ "e") map (_.text.toInt) // List(1, 2, 3, 4, 5)

 

val x0 = <users>

<user name="qh"><age>20</age></user>

<user name="james"><age>30</age></user>

</users>

(x0 \ "user") // <user name="qh"><age>20</age></user>, <user name="james"><age>30</age></user>)

(x0 \ "user" \ "age") // (<age>20</age>, <age>30</age>)

(x0 \ "age")  // 直接下級: ()

(x0 \\ "age") // 全部下級:(<age>20</age>, <age>30</age>)

(x0 \ "_") 全部

8.1.4.  訪問屬性

val x = <uu><u name="qh" /><u name="james" /><u name="qiu" /></uu>

(x \ "u" \\ "@name") foreach println // "qh\njames\nqiu"

 

例子:

val data =

<shopping>
  <item name="bread" quantity="3" price="2.50"/>
  <item name="milk" quantity="2" price="3.50"/>
</shopping>

val res = for (item <- data \ "item" ; 
                 price = (item \ "@price").text.toDouble ; 
                 qty = (item \ "@quantity").text.toInt)
           yield (price * qty)

printf("$%.2f\n", res.sum)

 

8.1.5.  格式化輸出

val pp = new xml.PrettyPrinter(80, 4)  // 行寬 80,縮進爲 4  

pp formatNodes <b><a/></b>  

結果是字符串 

<b> 

    <a></a> 

</b> 

 

8.2.     json

Scala-json

8.3.     Configgy

http://www.lag.net/configgy/

簡單配置及logging

----------------------------

log {

  filename = "/var/log/pingd.log"

  roll = "daily"

  level = "debug"

}

 

hostname = "pingd.example.com"

port = 3000

----------------------------

 

val conf = net.lag.configgy.Configgy.configure("/etc/pingd.conf").config

val hostname = conf.getString("hostname", "localhost")

val port = conf.getInt("port", 3000)

 

8.4.     正則表達式regex

例子1:徹底匹配

//將字符串轉爲scala.util.matching.Regex

val pattern = "^[a-z0-9._%\\-+]+@(?:[a-z0-9\\-]+\\.)+[a-z]{2,4}$"

val emailRegex = pattern.r // 或者 new scala.util.matching.Regex(pattern)

//emailRegex.pattern=>java.util.regex.Pattern 使用javaPattern

emailRegex.pattern.matcher("tt@16.cn").matches // true

 

例子2:查找匹配部分

val p = "[0-9]+".r  

p.findAllIn("2 ad 12ab ab21 23").toList // List(2, 12, 21, 23)

p.findFirstMatchIn("abc123xyz").get // scala.util.matching.Regex.Match = 123

 

更多例子以下:

定義:

val r1 = "(\\d+) lines".r  // syntactic sugar val r2 = """(\d+) lines""".r  // using triple-quotes to preserve backslashes
或者
import scala.util.matching.Regex val r3 = new Regex("(\\d+) lines")  // standard val r4 = new Regex("""(\d+) lines""", "lines") // with named groups

 

searchreplacejava.lang.String的方法):

"99 lines" matches "(\\d+) lines" // true
"99 Lines" matches "(\\d+) lines" // false
"99 lines" replace ("99", "98") // "98 lines"
"99 lines lines" replaceAll ("l", "L") // 99 Lines Lines

 

searchregex的方法):

"\\d+".r findFirstIn "99 lines" // Some(99)
"\\w+".r findAllIn "99 lines" // iterator(長度爲2)
"\\s+".r findPrefixOf "99 lines" // None
"\\s+".r findPrefixOf "  99 lines" // Some(  )
val r4 = new Regex("""(\d+) lines""", "g1") // with named groups
r4 findFirstMatchIn "99 lines-a" // Some(99 lines)
r4 findPrefixMatchOf "99 lines-a" // Some(99 lines)
val b = (r4 findFirstMatchIn "99 lines").get.group("g1") // "99"

 

matchregex的方法):

val r1 = "(\\d+) lines".r
val r4 = new Regex("""(\d+) lines""", "g1")
val Some(b) = r4 findPrefixOf "99 lines" // "99 lines" for {   line <- """|99 lines-a              |99 lines              |pass              |98 lines-c""".stripMargin.lines } line match {   case r1(n) => println("Has " + n + " Lines.") // "Has 99 Lines."
  case _ => }
 
for (matched <- "(\\w+)".r findAllIn "99 lines" matchData)
  println("Matched from " + matched.start + " to " + matched.end)

輸出:

Matched from 0 to 2

Matched from 3 to 8

 

replaceregex的方法):

val r2 = """(\d+) lines""".r  // using triple-quotes to preserve backslashes

r2 replaceFirstIn ("99 lines-a", "aaa") // "aaa-a"

r2 replaceAllIn ("99 lines-a, 98 lines-b", "bbb") // "bbb-a, bbb-b"

 

其餘:使用正則表達式定義變量

val regex = "(\\d+)/(\\d+)/(\\d+)".r

val regex(year, month, day) = "2010/1/13"

// year: String = 2010

// month: String = 1

// day: String = 13

 

8.5.     GUI

8.5.1.  java方式

import javax.swing.JFrame var jf = new JFrame("Hello!") jf.setSize(800, 600) jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) jf.setVisible(true)

 

8.5.2.  scala方式

import swing._, swing.Swing._

 

object Swing1 extends SimpleGUIApplication { // scala2.7

 

  def top = new MainFrame { // 必須實現top方法

    title = "窗口1"

    preferredSize = (400, 300)

   

    val label = new Label("hello world cn中文")

    contents = new BorderPanel {

      layout(label) = BorderPanel.Position.Center

    }

  }

}

 

9.   附錄

9.1.     stackoverflowscala教程

解答一些實際問題:

http://stackoverflow.com/tags/scala/info

9.2.     ProjectEuler

數學+編程答題:

http://projecteuler.net/index.php?section=problems

對上面問題的Haskell解答參考:

http://www.haskell.org/haskellwiki/Euler_problems

對上面問題的Python解答參考:

http://www.s-anand.net/euler.html

對上面問題的Scala解答參考:

http://pavelfatin.com/scala-for-project-euler/

 

9.3.     Scala問答

http://stackoverflow.com/questions/tagged/scala+list

 

9.4.     rosettacode

http://rosettacode.org/wiki/Category:Scala (各類語言解題彙編)

 

9.5.     編譯、mvnSBT

Maven 習筆記中的4SBT

Scala中相似於antmaven的東西,配置文件使用.scala,交互模式下使用,能夠自定編譯和測試。

vim/emacs/textmate等結合進行增量開發。

 

結合mvnsbt,能夠利用mvn eclipse:eclipse來產生.project.classpath,利用sbt的不間斷編譯運行,步驟:

l  運行maven命令(看mvnh.bat)建立目錄./prj1/結構:

mvn archetype:create -DgroupId=jamesqiu -DartifactId=prj1 -DpackageName=test

                             -------            ----------        ----------

                      項目在倉庫中的惟一標識   跟目錄名及jar        包名

l  運行sbt

問及name就填和mvnpackageName同樣(「test」)

問及organization就填和mvngroupId同樣(「jamesqiu」)

問及scala version就填2.8.0

l  運行mvn eclipse:eclipse生成eclipse項目文件

l  運行sbt

sbt ~compile 進行增量編譯;

sbt ~run 進行增量編譯運行,有多個可執行類時須要手工選擇。

 

9.6.     Scala水平劃分

來源:http://www.scala-lang.org/node/8610 Martin Odersky

應用開發者

庫開發者

Level A1: Beginning application programmer

·         Java-like statements and expressions: standard operators, method calls, conditionals, loops, try/catch

  • class, object, def, val, var, import, package
  • Infix notation for method calls
  • Simple closures
  • Collections with map, filter, etc

·         for-expressions

 

Level A2: Intermediate application programmer

·         Pattern matching

·         Trait composition

·         Recursion, in particular tail recursion

·         XML literals

Level L1: Junior library designer

·         Type parameters

·         Traits

·         Lazy vals

·         Control abstraction, currying

·         By-name parameters

Level A3: Expert application programmer

·         Folds, i.e. methods such as foldLeft, foldRight

·         Streams and other lazy data structures

·         Actors

·         Combinator parsers

Level L2: Senior library designer

·         Variance annotations

·         Existential types (e.g., to interface with Java wildcards)

·         Self type annotations and the cake pattern for dependency injection

·         Structural types (aka static duck typing)

·         Defining map/flatmap/withFilter for new kinds of for-expressions

·         Extractors

 

Level L3: Expert library designer

·         Early initializers

·         Abstract types

·         Implicit definitions

·         Higher-kinded types

 

9.7.     Scala適合的領域

http://goodstuff.im/scala-use-is-less-good-than-java-use-for-at-l by David Pollak

小結:

l  Scala更適合real-time, distributed, concurrent的應用,而不是ORMCRUD應用(儘管有Squeryl);

l  大部分的應用仍是ORMCRUD,與其推銷Scala5萬個項目裏只成功20%,還不如推薦到5千個合適項目裏成功80%

l  你的公司已高端人才爲主,推薦Scala;以堆人力爲主,仍是Java吧;

 

9.8.     TwitterScala School

http://twitter.github.com/scala_school/

訓練java expert快速成爲scala expert

相關文章
相關標籤/搜索