Scala的向後兼容性沒有Java那麼謹慎,2.8+相對於2.7有很大變化,本文檔針對2.8+ html
「和別人分享你的知識,那纔是永恆之道」——somebody java
前言: node
這只是份簡單的筆記,本不該敝履自珍,但手癢難耐仍是寫點廢話在前面。 react
書籍浩如煙海,技術方面的書也汗牛充棟,惋惜咱們的閱讀速度和理解力、記憶力太有限,每每費力學懂的知識轉眼就變得很是陌生,「博聞強志、過目不忘」者畢竟罕見。對於大部分人來講,即使昔日信手拈來的東西,時間久了也會毫無頭緒。因此知識不在於曾經學過多少,而在於你記住並還能運用多少。 git
「好記性不如爛筆頭」——近年來我慢慢習慣把費力學習的東西都作一個筆記,一是在學習的過程當中加深印象,畢竟技術學習不一樣於欣賞娛樂大片和瀏覽娛樂新聞看個過眼煙雲;二是便於學而「時習之」,書上的東西通常是針對不一樣技術背景的讀者,有不少做者費力用墨之處對你來講純屬廢話,而他一筆帶過的地方偏偏讓你困惑不已。一本讀書筆記至關於你對書的「註解」。 程序員
Scala很好玩頗有趣,但絕對不是一門簡單易懂的編程語言(Java從1.0到7.0,一直作++;而Scala不光作++,也作--)。對於從Java或者其餘FP走過來的人,Scala有不少「彆扭」的用法,很難記清楚用正確。學Scala,最佳的作法是把它用到平常的應用開發中,不斷加深記憶。但即使你準備這麼作了,手頭沒有一份方便的備查材料,剛開始也會步履艱難。我在使用的過程當中也有這個體會,因此纔不怨其煩地把一些學來並嘗試過的東西記在本文檔中備查,以便以後能行雲流水地「玩轉」它。整個寫筆記的過程,沒有孤寂,而是沉浸在學習新知的興奮和快意中,但願這種快樂也能放大傳遞給更多的人。 github
我的認爲,對於一門編程語言使用中的查閱,大體有幾個階段:查教程(tutorial)——》查手冊(handbook)——》查本身寫的庫。這個材料,不是嚴格的教程,或手冊,而是介於這二者間。Scala目前已經出版了幾本書,這些書從各自的角度解讀Scala,好比Scala做者的「Programming Scala」,應該是最權威的Scala書。但好書不必定好讀,一是太厚,二是無趣,不如自制的來得貼心。正如數學問題要用公式表達最清楚,編程問題得用圖表和代碼表示才最清楚,這兩者也是本文中使用最多的表達方式,我儘可能採用簡短的代碼來講明問題(簡短代碼也能說明不少事情,廣受讚譽的Effective Java基本沒有超過一頁的程序代碼)。可以熟練使用Java的程序員,參考本筆記,應該能夠自如地開始着手寫Scala程序。 正則表達式
若能給一樣對Scala感興趣的IT人一些幫助,本人必感欣慰。 算法
——JamesQiu
目錄
4.3. 協變和逆變(co-|contra-)variance
「Put productivity & creativity back in the hands of developers」
Scala is a tour de force of language design.
——基於JVM,和Java運行速度至關。看看Ruby、Perl、Python對大項目運行效率的無奈,就知道有個好的編譯器(Scalac)和運行時(JVM)是多麼美好。
——有更多的內建庫和數據結構,編程就更快,Scala在徹底繼承Java和.NET的標準庫的基礎上,還擴展了更豐富有用的函數庫。看看C++、D、Go等語言庫的發展狀況(不是匱乏就是混亂),就知道從頭建立如Java、.NET這般龐大全面的類庫並不是易事;
類庫和運行速度有關係嗎?——很大程度上有,衆多專家已經在類庫中準備了充分優化的穩定算法,Scala對Java Collection算法進行直接包裝或者直接調用,若是沒有豐富的類庫,你在項目週期內免不了摘抄一些不必定靠譜的算法和功能代碼,這些代碼極有可能在運行時給你帶來麻煩。使用類庫算法,不用擔心自造輪子的運行效率。
Scala是靜態語言,Scalac和Javac是同一做者,編譯成.class後運行於JVM平臺,近20年那麼多大公司投入進行的優化也不是白搭。對於大部分的應用來講,使用Scala不用再顧慮運行速度,它可能不是最快,但至少逼近Java,而不像Groovy、JRuby、Jython那般與Java有高達數十倍的效率差距。
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
l 適當地選用OOP或者FP,可以使表達相對另外一種更加清晰準確。
l 實際可見的生產力在於:一個應用中的部分代碼尤爲是高知識凝聚的代碼如數學函數和設計模式,通常來講不會本身編寫,而是會來自於現成的Java庫,或者其餘語言,或者僞代碼。咱們能夠很容易地把過程語言、面嚮對象語言、函數式語言中的代碼「翻譯」成Scala代碼。試想若是咱們要把Haskell或者Lisp的某個尾遞歸算法翻譯成Java代碼,還得多花點時間;而要把C++的代碼翻譯成Hashkell,一樣也不簡單。Scala的混血性給咱們的實際使用提供了便利。
l 語言特點可以塑造編程者的思惟: C++也能使用抽象基類設計多重繼承,但Java的接口引導你走得更遠;Java也能設計類型安全的靜態方法(final static),但Scala鼓勵你這樣作並逐步從OOP到達FP的彼岸,並且來去自如。
Scala雖然是一門徹頭完全的靜態語言,但又具有了現代動態語言的不少方便和靈活:
l 不須要冗餘的類型聲明
l 能夠在已有類上增長新方法(implicit轉換和Dynamic trait)
l 能夠把不繼承共同父類的不一樣類型對象傳到方法中
l 能夠作靜態語言的Refactoring
l 不用象動態語言那樣測試代碼比業務代碼還多
l 代碼自動完成(REPL和IDE)
l 編譯靜態語言的性能
l Read-Eval-Print Loop交互解釋器(注:Linux下的用戶體驗遠好於Windows下)
Scala能夠把xml/html處理、數學公式表達、SQL查詢等包裝的更優雅、更合理,爲使用者提供更好的API。這也使Scala的程序也更可讀,從而更易於維護。
不一樣的思考模式:Java是先寫後想,Scala是先想後寫(其實FP大都如此)。
Scala相比於Java,可能達不到10倍的代碼精簡;但讀Scala代碼的效率通常只有Java的1/10——可見Java是一門沒有多少特例的簡單語言,而Scala則否則。
你不要期望把Scala做爲初學者的第一門編程語言,這門語言甚至不是初級程序員可以掌控的——換句話說,可以讀懂和寫Scala代碼,說明你是一個徹徹底底的資深程序員,或者更準確一點,是資深Java程序員。
|
|
|
ð |
簡單的Java(Morse碼,定型的玩具車) |
複雜的Scala(智能機,樂高積木) |
還看這句話:「Put productivity & creativity back in the hands of developers」。其實不只限於Scala,對於全部的編程語言來講,一門語言是否「好玩」有趣,可否激起創做欲,纔是最關鍵的,這比語言風格、運行速度、工具支持、社區文化都來得重要。
回想我使用過的語言,C、C++,只有在學習「圖形學」課程作做業的時候給我「好玩」和編程過癮的感受;VB、Delphi、Lotus Script/Formular、JavaScript,重來沒有給過我「好玩」的感受,而Java是在以前很長一段時間內讓我以爲最「好玩」的語言,用它編遊戲、作模式識別的做業、作產品……,樂在其中。可是Java也許久沒有再給我這種編程過癮的感受了。以前發現Groovy的時候,我覺得又找到一門好玩的語言了,但我一段時間使用以後,發現不是個人菜(Perl、Python、Ruby也如此);我不是說那些我不以爲好玩的語言很差,有其餘不少人以爲他們很是「好玩」,而且用它們建立了無數殺手級的、偉大的、有用的程序。
有些人對一門語言會玩一生,就像Lisp、Haskell和Smalltalk的擁躉;而有些人會不斷尋找下一個玩意兒,就像原來玩Java的一些人發現更好玩的Ruby和Python以後,倒戈狂噴Java,力挺後者;Groovy/Grails的玩家在很短的時間裏面,寫了無數的擴展和Plugin應用;學習Scala,能不少好玩的地方,能用它有激情地去寫一些振奮人心的應用出來!
Scala開發/用戶社區氣氛良好,基本都是資深開發者以及有必定經驗的用戶,不會碰到太弱智的事(提問、爭吵),除了語言和工具開源免費,最權威和最好的書也都是免費的(包括Lift社區)
Java++:增長的語法 |
Java--:刪減的語法 |
純OO |
靜態成員 |
操做符重載 |
原生數據類型 |
closure |
break、continue |
使用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[_]
如下功能經過庫的形式提供:
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
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`()
注意:沒有break和continue
這些標識在Java中是非法的,在Scala中是合法的,能夠看成函數名使用,使接口更加DSL:
val empty_? = true
val + = "hello"
val `yield` = 10
val ** = "power"
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爲數組或者List,val的元素能夠賦值;
val pi = 3. // 至關於3.0d
val pi = 3.f // 至關於3.0f
提示:向函數式風格推動的一個方式,就是嘗試不用任何var來定義變量。
和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(a, b, _, _, c @ _*) = Array(1, 2, 3, 4, 5, 6, 7) // 也能夠用List,Seq
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
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 // 每次求值
儘可能使用大寫形式: Int, Long, Double, Byte, Short, Char, Float, Double, Boolean
編譯時Scala自動對應到Java原始類型,提升運行效率。Unit對應java的void
用 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統一了原生類型和引用類型。
-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)
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)
能夠表示很大的整數:
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
"..." 或者 """...""""
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
"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)
val sb = new StringBuilder
sb += 'H'
sb ++= "ello"
sb.toString // "Hello"
sb clear // StringBuilder()
使用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-30:james is 30.
"%2$d age's man %1$s: %2$d" format ("james", 30) // 30 age's man james: 30
Null |
Trait,其惟一實例爲null,是AnyRef的子類,*不是* AnyVal的子類 |
Nothing |
Trait,全部類型(包括AnyRef和AnyVal)的子類,沒有實例 |
None |
Option的兩個子類之一,另外一個是Some,用於安全的函數返回值 |
Unit |
無返回值的函數的類型,和java的void對應 |
Nil |
長度爲0的List |
Scala的==很智能,他知道對於數值類型要調用Java中的==,ref類型要調用Java的equals()
"hello"=="Hello".toLowerCase()
Scala的==老是內容對比
基本類型Int,Double, |
比值 |
其餘類型 |
至關於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 不是同一個引用
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或者列表操做 |
類比:Java的Stringx.split返回null |
類比:Java的Stringx.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]來統一處理 |
Some(3).getOrElse(4) // 3
None.getOrElse(4) // 4
例如打印key=3的value:
寫法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
例子1:
def m(k:Int) = {
Map((1,100),(2,200),(3,300)) get(k) match {
case Some(v) =>
k + ": " + v
case None =>
}
}
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))
<- |
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")) |
Java裏面的寫法:
switch(n) {
case(1): ...; break;
case(2): ...; break;
default: ...;
}
變成Scala寫法:
def m(n:String) =
n match {
case "a" | "b" => ... // 這個比較好
case "c" => ...
case _ => ...
}
每一個case..=>結束不用寫break了,_至關於default
match 能夠很簡單地匹配數據類型(不須要isInstanceOf[T]):
def f(v:Any) = v match {
case null => "null"
case i:Int => i*100
case s:String => s
case _ => "others"
}
注意:上面case中的i、s都叫模式變量
f(null) // "null"
f(5) // 500
f("hello") // "hello"
f(3.14) // "others"
注意:自定義類型若是也要匹配,須要用case class
/** 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?")
}
}
寫法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
寫法4(reduceLeft+!):
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
常量匹配很簡單,即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
寫法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)
})
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()
}
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
Scala的main方法(包括全部相似java的static方法)必須定義在一個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
不帶命令行參數的簡化main方法:
object app1 extends Application {
println("hello world")
}
Scala的import能夠只在局部做用域內生效;
能夠格式 「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
因爲Scala的package能夠是相對路徑下定義,有可能命名衝突,能夠用:
import _root_.java.lang.Long
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
Scala2.8+支持包對象(package object),除了和2.8以前同樣能夠有下級的object和class,還能夠直接有下級變量和函數,例如:
-------------------------------- 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就是一個包對象,a和b就是包p1直屬的常量和函數,
$fsc foo.scala 命令產生以下class:
./p0/p1/package.class
調用:
scala p0.p1.package
沒有java的:
b = (x>y) ? 100 : -1
就用:
if (x>y) 100 else -1
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 |
循環中的變量不用定義,如:
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, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5 , 6, 5, 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))
把每次循環的結果「移」進一個集合(類型和循環內的一致)
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不能在任何括號內
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
"全部都符合"——至關於 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
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)
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)
累加或累乘
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
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)))
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
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中的函數要返回Boolean,sum<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))
Scala中沒有break和continue語法!須要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)
例子2:1 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) }
注意:其實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)
scala._自動加載,只有發生類名衝突時才須要帶上scala.包名。
scala.AnyValue |
全部基本類型的根 |
Int,Char,Boolean,Double,Unit |
scala.AnyRef |
全部引用類型的根 |
至關於java的java.lang.Object |
scala.Null |
全部引用類型的子類 |
|
scala.Nothing |
全部所有類型的子類 |
|
scala.List |
不可變的List |
scala特點的不可變List |
scala.Int |
scala中能夠用int做爲別名 |
Double,Float等相似 |
用途:
l 把一種object類型安全地自動轉換成另外一種object類型;
l 不改動已有class設計便可添加新的方法;
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
第一步:寫函數
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!)
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
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
object NewMethod {
// 定義新方法join()
implicit def foo1[T](list: List[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:定義如ruby的10.next
implicit def foo(n:Int) = new { def next = n+1 }
10.next // 11
至關於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也能夠作泛型
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"
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"
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(_<_)
// 採用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
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
def test[@specialized(Int,Double) T](x:T) = x match { case i:Int => "int"; case _ => "other" }
沒有@specialized以前,是編譯成Object的代碼;用了@specialized,變成IntTest(),DoubleTest(),...
編譯後的文件尺寸擴充了,但性能也強了,不用box,unbox了
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藍色
函數的地位和通常的變量是同等的,能夠做爲函數的參數,能夠做爲返回值。
傳入函數的任何輸入是隻讀的,好比一個字符串,不會被改變,只會返回一個新的字符串。
Java裏面的一個問題就是不少只用到一次的private方法,沒有和使用它的方法緊密結合;Scala能夠在函數裏面定義函數,很好地解決了這個問題。
函數和方法通常用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或者值 |
提示:函數式風格——儘可能編寫有返回值的函數; 儘可能簡短(函數體不使用{...})
一種特殊的定義:映射式定義(直接至關於數學中的映射關係);
其實也能夠當作是沒有參數的函數,返回一個匿名函數;調用的時候是調用這個返回的匿名函數。
例子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
方法名能夠是*:
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
注意:從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
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
形式:((命名參數列表)=>函數實現)(參數列表)
特殊地:
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") )
用下劃線代替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函數。
_:匿名函數中的匿名參數,同Groovy的it同樣,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) // 錯誤!_不在最後不能省
變長參數只能放在最後一個,不然就歧義了,
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)
就是調用時用到函數的該參數,每次都從新計算:
例子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)
首選使用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 } |
求a,b的最大公約數,全部的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" |
採用映射式函數定義:
例子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)的簡寫
def f(s:String):(Int => String) = { n:Int=> s * n }
f("*") // res32: (Int) => String = <function1>
res32(10) // "**********"
f("*")(10) // 至關於先獲得f("*")的返回值函數,再用該返回值函數調用一個參數
例如:
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)
使用遞歸的緣由:
其一是不少數學關係、邏輯關係自己就是遞歸描述(Hanoi、Fib)的;
其二是函數式編程不鼓勵用變量循環,而是用遞歸。
遞歸包含遞歸出口和遞歸體:
遞歸出口 |
即遞歸的終止條件 好比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
例子2:Hanoi漢諾塔
從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-1和N-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) }
定義:函數尾(最後一條語句)是遞歸調用的函數。
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) |
nn2(5, 1) |
不算,直到遞歸到一個肯定的值後,又從這個具體值向後計算;更耗資源,每次重複的調用都使得調用鏈條不斷加長. 系統使用棧進行數據保存和恢復 |
每遞歸一次就算出相應的結果。 |
不能優化成循環 |
能夠優化成循環 |
例子:查找第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) // 第100個fib數=354224848179261915075
例子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定義後纔是屬性,ln,fn,s不可見
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();
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 }
}
不一樣於Java的使用 @Override,或者直接使用相同名字覆蓋父類方法。
override def toString = { name + "," + age + "," + female }
若是是覆蓋抽象方法,能夠不用overriade關鍵字。
如:
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"
|
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) }
}
import ImportSub._
object ImportMain {
def main(args : Array[String]) : Unit = {
println(5!) // 調用ImportSub中定義的implicit函數
}
}
例如:
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>
(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)
好比定義一個標識類(而不是字符串):
case object Start
case object Stop
Java中:
enum fruits { apple, banana, cherry }
在Scala中,則是:
sealed abstract class Fruits // sealed相似於java的final
case object Apple extends Fruits
case object Banana extends Fruits
case object Cherry extends Fruits
也能夠是 case class
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方法):
@reflect.BeanProperty var name = "anonymous"
}
val o2 = new c2
o2.name = "qh" // 也能夠直接存取
o2.name // "qh"
o2.setName("james") // 增長了set/get方法
o2.getName() // "james"
Scala沒有太特別的反射機制,使用java的便可,不過Scala在match..case中能夠匹配類型:
case o:FooClass1 => ...
相關還有isInstanceOf[T], asInstanceOf[T]
例1(利用java的reflect):
"hello".getClass.getMethods.map(_.getName).toList.sortWith(_<_).mkString(", ")
例子2:
classOf[String] // 至關於java中的String.class
"aaa".isInstanceOf[String] // true
"aaa".asInstanceOf[String]
注:trait [treit] n.特徵,特色,特性
和Java的Interface相似,但能夠定義實體方法,而非僅僅方法定義
trait能夠看做有方法實現和字段的interface;表明一類事物的特性;
好比
Tom,多是Engine 和Son兩個trait的混合;
Sunny可能Sales、Son和Father三個trait的混合;
當在運行時往Son裏面增長方法或者字段的時候,Tom和Sunny都獲得增長的特性。
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 {...}
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)
使用「+」「-」差別標記
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] |
-A是A的子集,叫逆變
+B是B的超集,叫協變
|
|
<% |
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類型範圍大於A,A爲下界 |
例子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 // 非法
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 |
Array |
長度固定 |
元素可變 |
肯定長度,後賦值; |
List |
長度固定 |
元素不可變 |
|
Tuple |
長度固定 |
元素不可變 |
經常使用於有多個返回值的函數;或者多個變量的同時定義 |
Scala 2.8中,3者的元素均可以混合不一樣的類型(轉化爲Any類型);
Scala 2.7中,Array、List都不能混合類型,只有Tuple能夠;
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")
定義一個類型爲Any的Array:
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()
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()
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)
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")
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)
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後生成一箇中間collection,size=50000000/13;然後者不生成此中間collection,只計算到26便可。
例子2:
(1 to 100).map(i=> i*3+7).filter(i=> (i%10)==0).sum // map和filter生成兩個中間collection
(1 to 100).toStream.map(i=> i*3+7).filter(i=> (i%10)==0).sum
先進後出的堆棧:
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()
// 類型能夠混合:
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, 相似於Groovy的it
// = List(A, B, C)
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)
注: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)))
打印:
Array("a","b","c","d").foreach(printf("[%s].",_))
// [a].[b].[c].[d].
// 集合中是否存在符合條件的元素
List(1,2,3,4,5).exists(_%3==0) // true
返回序列中符合條件的第一個。
例子:查找整數的第一個因子(最小因子、質數)
def fac1(n:Int) = if (n>= -1 && n<=1) n else (2 to n.abs) find (n%_==0) get
例子(排序):
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))
例子:(去除List中的重複元素)
def uniq[T](l:List[T]) = l.distinct
uniq(List(1,2,3,2,1)) // List(1,2,3)
flatMap的做用:把多層次的數據結構「平面化」,並去除空元素(如None)。
可用於:獲得xml等樹形結構的全部節點名稱,去除None等
List(1,2,3) * List(10,20,30) = List(10, 20, 30, 20, 40, 60, 30, 60, 90)
val (a,b) = (List(1,2,3), List(10,20,30))
a flatMap (i=> b map (j=> i*j))
等同於:
for (i<-a; i<-b) yield i*j // 這個寫法更清晰
例子1b:
若是不用flatMap而是用map,結果就是:
a 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))
例子3:flatMap結合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))
獲得indices:
val a = List(100,200,300)
a indices // (0,1,2)
a zipWithIndex // ((100,0), (200,1), (300,2))
(a indices) zip a // ((0,100), (1,200), (2,300))
截取一部分,至關於String的substring
List(100,200,300,400,500) slice (2,4) // (300,400), 取l(2), l(3)
List(1,3,5,7) take 2 // List(1,3)
List(1,3,5,7) drop 2 // List(5,7)
知足條件的元素數目:
例如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
對於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不變
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*)
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,長度相同且每一個對應項符合判斷條件
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)
例子:獲得 (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 }
對其餘語言去重感興趣,可看看:
http://rosettacode.org/wiki/Remove_duplicate_elements
統一使用(),而不是[],()就是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"
在某類型的集合對象上調用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)
方案一:Java的List<T>很容易經過List.toArray轉換到Array,和Scala中的Array是等價的,可以使用map、filter等。
方案二:使用第三方的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還能夠在java的collection上使用foreach (目前除foreach外,還不支持filter、map)
a1.foreach(println)
scalaj的簡易文檔以下:
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] |
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有「拉拉鍊」的意思,就是把兩排鏈釦徹底對應扣合在一塊兒,很是形象。
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自己不可改變,即便定義爲var,update也是返回一個新的不可變Map:
var m4 = Map(1->100)
val m5 = m4(1)=1000 // m4仍是 1->100, m5:1->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))
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))
使用Java的HashMap:
val m1:java.util.Map[Int, String] = new java.util.HashMap
上面說過,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)
也能夠進行filter、map操做:
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=2,3有值
結合Map和Tuple,很容易實現一個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)
注:BitSet(collection.immutable.BitSet)和Set相似,但操做更快
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)
運算 |
例子 |
交集 |
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) |
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()
Iterator不屬於集合類型,只是逐個存取集合中元素的方法:
val it = Iterator(1,3,5,7) // Iterator[Int] = non-empty iterator
it foreach println // 1 3 5 7
it foreach println // 無輸出
三種經常使用的使用模式:
val it = Iterator(1,3,5,7)
while(it.hasNext) println(it.next)
// 2、使用for
for(e<- Iterator(1,3,5,7)) println(e)
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
// 在使用a、b前,不能使用it,不然a、b都不可用了。
a toList // List(1,3,5,7)
b toList // List(1,3,5,7)
// 此時it也不可用了
Scala 2.9+引入:
(1 to 10).par foreach println
多運行幾回,注意打印順序會有不一樣
scala特有的是scala.io.Source,例如:
import scala.io._
Source.fromFile("cn.scala","utf8").mkString
Source.fromFile(new java.io.File("cn.scala")).getLines().foreach(println)
直接調用java的io:
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
直接調用java的io:
val in = new FileInputStream("in").getChannel
val out = new FileOutputStream("out").getChannel
in transferTo (0, in.size, out)
遞歸使用listFiles:
import java.io.File
class Dir(file:File) {
// 目錄則返回全部1級子文件;文件則返回empty
def child = new Iterable[File] {
// Iterable接口必須實現elements方法
def elements = if (file.isDirectory) file.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<-list) println(f)
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
http://www.scala-lang.org/docu/files/actors-api/actors_api_guide.html#
Scala中處理併發,有不少選擇:
l actor消息模型,相似Erlang,首選,Lift和akka也實現了本身的actor模型。
l Thread、Runnable
l java.util.concurennt
l 3rd併發框架如Netty,Mina
Java內置線程模型 |
Scala actor模型 |
「共享數據-鎖」模型(share data and lock) |
share nothing |
每一個object有一個monitor,監視多線程對共享數據的訪問 |
不共享數據,actor之間經過message通信 |
加鎖的代碼段用synchronized標識 |
|
死鎖問題 |
|
每一個線程內部是順序執行的 |
每一個actor內部是順序執行的 |
|
|
對好比下的算法:
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的寫法,也能夠用Scala的actor寫法:
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)
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 中的下一條消息。 |
特色:要反覆處理消息,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 :("
特色:
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)
}
}
}
scala> self ! "hello"
scala> self.receive { case x => x }
scala> self.receiveWithin(1000) { case x => x }
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秒鐘後打印5個wakeup
-----------------------------------------
actor模型讓咱們寫多線程程序時只用關注各個獨立的單線程程序(actor),他們之間經過消息來通信。例如,若是BadActor中有一個GoodActor的引用:
class BadActor(a:GoodActor) extends Actor {...}
那在BadActor中便可以經過該引用來直接調用GoodActor的方法,也能夠經過「!」來傳遞消息。選擇後者!由於一旦BadActor經過引用讀取GoodActor實例的私有數據,而這些數據可能正被其餘線程改寫值,結果就避免不了「共享數據-鎖」模型中的麻煩事:即必須保證BadActor線程讀取GoodActor的私有數據時,GoodActor線程在這塊成爲「共享數據」的操做上加鎖。GoodActor只要有了共享數據,就必須來加鎖防範競用衝突和死鎖,你又得從actor模型退回到「共享數據-鎖」模型(注:actor對消息是順序處理的,原本不用考慮共享數據)。
Scala的actor模型讓每一個actor的act方法內部接近於單線程環境,你不用小心act方法裏面的操做是否線程安全。在act方法中你能夠盡情使用非同步、可變對象,由於每一個act方法被有效限制在單個線程中,這也是actor模型被稱爲「share-nothing」 模型(零共享模型)的緣由,其數據的做用範圍被限制在單個線程中。不過一旦對象內的數據被用於多個actor之間進行消息傳遞。這時你就必須考慮消息對象是否線程安全。
保證消息對象線程安全的最好方法就是保證只使用不可變對象做爲消息對象。消息類中只定義val字段,且只能指向不可變對象。定義這種不可變消息類的簡單方法就是使用case class, 並保證其全部的val字段都是不可變的。Scala API中提供了不少不可變對象可用,例如基本類型、String、Tuple、List,不可變Set、不可變Map等。
若是你發現確實須要把一個可變對象obj1發送給其餘actor,也因該是發送一份拷貝對象obj1.clone過去,而不是把obj1直接發過去。例如,數據對象Array是可變且未作同步的,因此Array只應該由一個actor同時存取,若是須要發送數組arr,就發送arr.clone(arr中的元素也應該是不可變對象),或者直接發送一個不可變對象arr.toList更好。
總結:大部分時候使用不可變對象很方便,不可變對象是並行系統的曙光,它們是易使用、低風險的線程安全對象。當你未來要設計一個和並行相關的程序時,不管是否使用actor,都應該儘可能使用不可變的數據結構。
對每一種消息建立一個對應的case class,而不是使用上面的tuple數據結構。雖然這種包裝在不少狀況下並不是必須,但該作法能使actor程序易於理解,例如:
// 不易理解,由於傳遞的是個通常的字符串,很難指出那個actor來響應這個消息
lookerUpper ! ("www.scala-lang.org", self)
// 改成以下,則指出只有react能處理LoopupIP的actor來處理:
case class LookupIP(hostname: String, requester: Actor)
lookerUpper ! LookupIP("www.scala-lang.org", self)
服務器端:
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('server1, Actor.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")
}
}
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 { _ == ' ' } }
Scala原生支持xml,就如同Java支持String同樣,這就讓生成xml和xhtml很簡單優雅:
val name = "james"
val age = 10
val html = <html>name={name}, age="{age}"</html> toString
// <html>name=james, age="10"</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>
xml.XML loadString "<p></p>"
xml.XML loadFile "abc.xml"
xml.XML.saveFull("foo.xml", node, "UTF-8",
xmlDecl: Boolean, doctype : DocType)
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 \ "_") 全部
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)
val pp = new xml.PrettyPrinter(80, 4) // 行寬 80,縮進爲 4
pp formatNodes <b><a/></b>
結果是字符串
<b>
<a></a>
</b>
Scala-json
簡單配置及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)
例子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 使用java的Pattern
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
search和replace(java.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
search(regex的方法):
"\\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"
match(regex的方法):
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
replace(regex的方法):
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
import javax.swing.JFrame var jf = new JFrame("Hello!") jf.setSize(800, 600) jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) jf.setVisible(true)
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
}
}
}
解答一些實際問題:
http://stackoverflow.com/tags/scala/info
數學+編程答題:
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/
http://stackoverflow.com/questions/tagged/scala+list
http://rosettacode.org/wiki/Category:Scala (各類語言解題彙編)
見「Maven 學習筆記」中的4、SBT
Scala中相似於ant和maven的東西,配置文件使用.scala,交互模式下使用,能夠自定編譯和測試。
和vim/emacs/textmate等結合進行增量開發。
結合mvn和sbt,能夠利用mvn eclipse:eclipse來產生.project和.classpath,利用sbt的不間斷編譯運行,步驟:
l 運行maven命令(看mvnh.bat)建立目錄./prj1/結構:
mvn archetype:create -DgroupId=jamesqiu -DartifactId=prj1 -DpackageName=test
------- ---------- ----------
項目在倉庫中的惟一標識 跟目錄名及jar名 包名
l 運行sbt
問及name就填和mvn的packageName同樣(「test」)
問及organization就填和mvn的groupId同樣(「jamesqiu」)
問及scala version就填2.8.0
l 運行mvn eclipse:eclipse生成eclipse項目文件
l 運行sbt
sbt ~compile 進行增量編譯;
sbt ~run 進行增量編譯運行,有多個可執行類時須要手工選擇。
來源: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
· 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 |
http://goodstuff.im/scala-use-is-less-good-than-java-use-for-at-l by David Pollak
小結:
l Scala更適合real-time, distributed, concurrent的應用,而不是ORM、CRUD應用(儘管有Squeryl);
l 大部分的應用仍是ORM和CRUD,與其推銷Scala到5萬個項目裏只成功20%,還不如推薦到5千個合適項目裏成功80%;
l 你的公司已高端人才爲主,推薦Scala;以堆人力爲主,仍是Java吧;
http://twitter.github.com/scala_school/
訓練java expert快速成爲scala expert