隨着業務和數據的須要,咱們引入了Spark
。Spark
對Python
的支持仍是挺好的,但畢竟它仍是使用Scala
開發的,且現有的API並無100%覆蓋Python。因此就有了這篇文章,讓Python程序員能夠接觸Scala
這門更高(級)、更快(速)、更強(大)的(奧運精神)語言。html
Scala兼具Python樣的開發效率,但又有Java般的執行性能,真是一不可多得的神器!(固然,魚和熊不可兼得,Scala的入門曲線相比Python是要那麼陡峭一丟丟)程序員
通常Linux系統都自帶Python
環境,但Scala是沒有的。這須要咱們手動安裝,還須要安裝Java環境。Java環境的安裝這裏就不介紹了,網上不少。說說Scala的安裝吧。下載地址在http://scala-lang.org/download/2.11.7.html。es6
wget -c http://downloads.typesafe.com/scala/2.11.7/scala-2.11.7.tgz tar zxf scala-2.11.7 cd scala-2.11.7 ./bin/scala Welcome to Scala version 2.11.7 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_60). Type in expressions to have them evaluated. Type :help for more information. scala>
咱們能夠看到終端出現了scala>
提示符,這個就像Python
的REPL
同樣,Scala也擁有一個REPL
。咱們能夠在這裏很方便的編寫一些代碼,測試一些想法。正則表達式
對於Scala經常使用的IDE(集成開發環境),推薦使用IDEA for scala plugins和scala-ide。spring
Scala的強大,除了它自身對多核編程更好的支持、函數式特性及一些基於Scala的第3方庫和框架(如:Akka、Playframework、Spark、Kafka……),還在於它能夠無縫與Java結合。全部爲Java開發的庫、框架均可以天然的融入Scala環境。固然,Scala也能夠很方便的Java環境集成,好比:Spring。若你須要第3方庫的支持,可使用Maven、Gradle、Sbt等編譯環境來引入。express
Scala是一個面向對象的函數式特性編程語言,它繼承了Java的面向對特性,同時又從Haskell
那裏吸取了不少函數式特性並作了加強。apache
一般狀況下,Java系的程序都須要有一個main方法來執行。並須要放入一個Web container
容器,或打成一個jar
包來執行。可是Scala不同,它除了支持傳統的Java方式,它也能夠像Python同樣把代碼保存到一個腳本文件裏(.scala)執行,就像一個Shell腳本同樣。編程
yangjing-mac-air:scala-2.11.7 jingyang$ cat test.scala #!/bin/sh exec scala "$0" "$@" !# // Say hello to the first argument println("Hello, "+ args(0) +"!") yangjing-mac-air:scala-2.11.7 jingyang$ ./test.scala 楊景 Hello, 楊景!
(注:須要把$SCALA_HOME/bin
加入系統環境變量才能直接執行scala
命令)安全
能夠看到,使用Scala,你除了像傳統的Java程序同樣把它作爲一個「服務」的方式來啓動,你也能夠把它像Python同樣作爲一個「腳本」來啓動。框架
(注意:Scala不像Python同樣經過代碼縮進來表示代碼的層次關係,而是和一般的語言同樣使用{}
來表示代碼的層級。給程序員更多的自由)
Scala中變量不須要顯示指定類型,但須要提早聲明。這能夠避免不少命名空間污染問題。Scala有一個很強大的類型自動推導功能,它能夠根據右值及上下文自動推導出變量的類型。你能夠經過以下方式來直接聲明並賦值。
scala> val a = 1 a: Int = 1 scala> val b = true b: Boolean = true scala> val c = 1.0 c: Double = 1.0 scala> val a = 30 + "歲" a: String = 30歲
Immutable
(注:函數式編程有一個很重要的特性:不可變性。Scala中除了變量的不可變性,它還定義了一套不可變集合scala.collection.immutable._
。)
val
表明這是一個final variable,它是一個常量。定義後就不能夠改變,相應的,使用var
定義的就是日常所見的變量了,是能夠改變的。從終端的打印能夠看出,Scala從右值自動推導出了變量的類型。Scala能夠如動態語言似的編寫代碼,但又有靜態語言的編譯時檢查,不會像Python同樣留下不少陷阱在運行多時之後才被發現。
(注:在RELP
中,val
變量是能夠從新賦值的,這是` RELP`的特性。在日常的代碼中是不能夠的。)
基礎數據類型
Scala中基礎數據類型有:Byte、Short、Int、Long、Float、Double,Boolean,Char、String。和Java不一樣的是,Scala中沒在區分原生類型和裝箱類型,如:int
和Integer
。它統一抽象成Int
類型,這樣在Scala中全部類型都是對象了。編譯器在編譯時將自動決定使用原生類型仍是裝箱類型。
字符串
Scala中單引號和雙引號包裹是有區別的,單引號用於字符,雙引號用於字符串。
scala> val c1 = 'c' c1: Char = c scala> val 字符2 = '楊' 字符2: Char = 楊 scala> val s1 = "杭州譽存科技有限公司" s1: String = 杭州譽存科技有限公司 scala> val s2 = s"杭州譽存科技有限公司工程師${c2}景" s2: String = 杭州譽存科技有限公司工程師楊景 scala> val s3 = s"""杭州譽存科技有限公司"工程師"\n${c2}景是江津人""" s3: String = 杭州譽存科技有限公司"工程師" 楊景是江津人
Scala基於JVM平臺,默認使用unicode,因此變量名是能夠直接用中文的。而在Scala中,中文也是直接顯示的,不像Python2同樣會輸出成unicdoe編碼形式:\uxxxx。
Scala還支持String Interpolation(「字符串插值")的特性,可使用${variable name}
這樣的形式引用變量,並將值插入。就像示例s2
同樣。
而連線3個雙引號在Scala中也有特殊含義,它表明被包裹的內容是原始字符串,能夠不須要字符轉碼。這一特性在定義正則表達式時頗有優點。
Scala中的運算符實際上是定義在對象上的方法(函數),你看到的諸如:3 + 2
實際上是這樣子的:3.+(2)
。+
符號是定義在Int
對象上的一個方法。支持和Java一至的運算符(方法):
(注:在Scala中,方法前的.
號和方法兩邊的小括號在不引發歧義的狀況下是能夠省略的。這樣咱們就能夠定義出很優美的DSL
)
==
、!=
:比較運算!
、|
、&
、^
:邏輯運算>>
、<<
:位運算在Scala中,修正(算更符合通常人的常規理解吧)==
和!=
運算符的含義。在Scala中,==
和!=
是執行對象的值比較,至關於Java中的equals
方法(實際上編譯器在編譯時也是這麼作的)。而對象比較須要使用eq
和ne
兩個方法來實現。
Scala中支持if
、while
、for comprehension
(for表達式)、match case
(模式匹配)四大主要控制語句。Scala不支持switch
和? :
兩種控制語句,但它的if
和match case
會有更好的實現。
if
Scala支持if
語句,其基本使用和Java
、Python
中的同樣。但不一樣的時,它是有返回值的。
(注:Scala是函數式語言,函數式語言還有一大特性就是:表達式。函數式語言中全部語句都是基於「表達式」的,而「表達式」的一個特性就是它會有一個值。全部像Java
中的? :
3目運算符可使用if
語句來代替)。
scala> if (true) "真" else "假" res0: String = 真 scala> val f = if (false) "真" else "假" f: String = 假 scala> val unit = if (false) "真" unit: Any = () scala> val unit2 = if (true) "真" unit2: Any = 真
能夠看到,if
語句也是有返回值的,將表達式的結果賦給變量,編譯器也能正常推導出變量的類型。unit
和unit2
變量的類型是Any
,這是由於else
語句的缺失,Scala編譯器就按最大化類型來推導,而Any
類型是Scala中的根類型。()
在Scala中是Unit
類型的實例,能夠看作是Java
中的Void
。
while
Scala中的while
循環語句:
while (條件) { 語句塊 }
for comprehension
Scala中也有for
表達式,但它和Java
以及Python
中的for
不太同樣,它具備更強大的特性。一般的for
語句以下:
for (變量 <- 集合) { 語句塊 }
Scala中for
表達式除了上面那樣的常規用法,它還可使用yield
關鍵字將集合映射爲另外一個集合:
scala> val list = List(1, 2, 3, 4, 5) list: List[Int] = List(1, 2, 3, 4, 5) scala> val list2 = for (item <- list) yield item + 1 list2: List[Int] = List(2, 3, 4, 5, 6)
還能夠在表達式中使用if
判斷:
scala> val list3 = for (item <- list if item % 2 == 0) yield item list3: List[Int] = List(2, 4)
還能夠作flatMap
操做,解析2維列表並將結果攤平(將2維列表拉平爲一維列表):
scala> val llist = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9)) llist: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9)) scala> for { | l <- llist | item <- l if item % 2 == 0 | } yield item res3: List[Int] = List(2, 4, 6, 8)
看到了,Scala中for comprehension
的特性是很強大的。它和Python
中的list comprehension
很相似,但不一樣的是在Scala中這一特性並不僅限於list
中,而是整個集合庫都支持這一特性,包括:Seq
、Map
、Set
、Array
……
Scala和Python同樣,也沒有C-Like語言裏的for (int i = 0; i < 10; i++)
語法,但和Python相似的它也有xrange
或range
函數樣的效果。在Scala中的使用方式以下:
scala> for (i <- (0 until 10)) { | println(i) | } 0 1 2 3 4 5 6 7 8 9
比Python更好的一點時,Scala中還有一個to
方法(Scala中去處符其實都是定義在對象上的方法/函數):
scala> for (i <- (0 to 10)) print(" " + i) 0 1 2 3 4 5 6 7 8 9 10
match case
模式匹配,是函數式語言很強大的一個特性。它比命令式語言裏的switch
更好用,表達性更強。
scala> def level(s: Int) = s match { | case n if n >= 90 => "優秀" | case n if n >= 80 => "良好" | case n if n >= 70 => "良" | case n if n >= 60 => "及格" | case _ => "差" | } level: (s: Int)String scala> level(51) res28: String = 差 scala> level(93) res29: String = 優秀 scala> level(80) res30: String = 良好
能夠看到,模式匹配可使用switch
相同的功能。但也switch
須要使用break
明確告知終止以後的判斷不一樣,Scala中的match case
是默認break的,只要其中一個case
語句匹配,就終止以後的因此比較。且對應case
語句的表達式值將做爲整個match case
表達式的值返回。
Scala中的模式匹配還有類型匹配、數據抽取、謂詞判斷等其它有用的功能。這裏只作簡單介紹,以後會單獨一個章節來作較詳細的解讀。
在Python中,經常使用的集合類型有:list
、tuple
、set
、dict
。Scala中對應的有:List
、Tuple[X]
、Set
、Map
。
List
Scala中List
是一個遞歸不可變集合,它很精妙的使用遞歸結構定義了一個列表集合。除了以前使用List
object來定義一個列表,還可使用以下方式:
scala> val list = 1 :: 2 :: 3 :: 4 :: 5 :: Nil list: List[Int] = List(1, 2, 3, 4, 5)
List
採用前綴操做的方式(全部操做都在列表頂端(開頭))進行,::
操做符的做用是將一個元素和列表鏈接起來,並把元素放在列表的開頭。這樣List
的操做就能夠定義成一個遞歸操做。添加一個元素就是把元素加到列表的開頭,List只須要更改下頭指針,而刪除一個元素就是把List的頭指針指向列表中的第2個元素。這樣,List
的實現就很是的高效,它也不須要對內存作任何的轉移操做。List
有不少經常使用的方法:
scala> list.indexOf(3) res6: Int = 2 scala> 0 :: list res8: List[Int] = List(0, 1, 2, 3, 4, 5) scala> list.reverse res9: List[Int] = List(5, 4, 3, 2, 1) scala> list.filter(item => item == 3) res11: List[Int] = List(3) scala> list res12: List[Int] = List(1, 2, 3, 4, 5) scala> val list2 = List(4, 5, 6, 7, 8, 9) list2: List[Int] = List(4, 5, 6, 7, 8, 9) scala> list.intersect(list2) res13: List[Int] = List(4, 5) scala> list.union(list2) res14: List[Int] = List(1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9) scala> list.diff(list2) res15: List[Int] = List(1, 2, 3)
Scala中默認都是Immutable collection,在集合上定義的操做都不會更改集合自己,而是生成一個新的集合。Python中只有set
上有求交、並、差積運算,Scala中將其範化到因此序列集合上(Seq
、List
、Set
、Array
……)均可以支持。
Tuple
Scala中也支持Tuple(元組)這種集合,但最多隻支持22個元素(事實上Scala中定義了Tuple0
、Tuple1
……Tuple22
這樣22個TupleX
類,實現方式與C++ Boost
庫中的Tuple
相似)。和Python中相似,Scala也採用小括號來定義元組。
scala> val tuple1 = (1, 2, 3) tuple1: (Int, Int, Int) = (1,2,3) scala> tuple1._2 res17: Int = 2 scala> val tuple2 = Tuple2("楊", " ) tuple2: (String, String) = (楊,景)
可使用xxx._[X]
的形式來引用Tuple
中某一個具體元素,其_[X]
下標是從1開始的,一直到22(如有定義這麼多)。
Set
Set
是一個不重複且無序的集合,初始化一個Set
須要使用Set
對象:
scala> val set = Set("Python", "Scala", "Java", "C++", "Javascript", "C#", "PHP") set: scala.collection.immutable.Set[String] = Set(Scala, C#, Python, Javascript, PHP, C++, Java) scala> set + "Go" res21: scala.collection.immutable.Set[String] = Set(Scala, C#, Go, Python, Javascript, PHP, C++, Java) scala> set filterNot (item => item == "PHP") res22: scala.collection.immutable.Set[String] = Set(Scala, C#, Python, Javascript, C++, Java)
Map
Scala中的Map
是一個HashMap,其key
也是無序的。Python中的dict
對應些類型(可使用TreeMap
來讓Map有序)。與Python中不同,Scala並無提供一個{}
來定義Map
,它仍是很統一的採用相關類型的object來定義:
scala> val map = Map("a" -> "A", "b" -> "B") map: scala.collection.immutable.Map[String,String] = Map(a -> A, b -> B) scala> val map2 = Map(("b", "B"), ("c", "C")) map2: scala.collection.immutable.Map[String,String] = Map(b -> B, c -> C)
Scala中定義Map
時,傳入的每一個Entry
(K、V對)其實就是一個Tuple2
(有兩個元素的元組),而->
是定義Tuple2
的一種便捷方式。
scala> map + ("z" -> "Z") res23: scala.collection.immutable.Map[String,String] = Map(a -> A, b -> B, z -> Z) scala> map.filterNot(entry => entry._1 == "a") res24: scala.collection.immutable.Map[String,String] = Map(b -> B) scala> map res25: scala.collection.immutable.Map[String,String] = Map(a -> A, b -> B)
Scala的immutable collection並無添加和刪除元素的操做,其定義+
(List
使用::
在頭部添加)操做都是生成一個新的集合,而要刪除一個元素通常使用.filterNot
函數來映射一個新的集合實現。
(注:Scala中也scala.collection.mutable._
集合,它定義了不可變集合的相應可變集合版本。通常狀況下,除非一性性能優先的操做(其實Scala集合採用了共享變量的優化,生成一個新集合並不會生成全部元素的副本,它將會和老的集合共享大元素。由於Scala中變量默認都是不可變的),推薦仍是採用不可變集合。由於它更直觀、線程安全,你能夠肯定你的變量不會在其它地方被不當心的更改。)
在Scala中,函數是一等公民。函數能夠像類型同樣被賦值給一個變量,也能夠作爲一個函數的參數被傳入,甚至還能夠作爲函數的返回值返回(這就是函數式編程)。
他Python同樣,Scala也使用def
關鍵詞來定義一個函數:
scala> def calc(n1: Int, n2: Int): (Int, Int) = { | (n1 + n2, n1 * n2) | } calc: (n1: Int, n2: Int)(Int, Int) scala> val (add, sub) = calc(5, 1) add: Int = 6 sub: Int = 5
這裏定義了一個函數:calc
,它有兩個參數:n1
和n2
,其類型爲:Int
。cala
函數的返回值類型是一個有兩個元素的元組,在Scala中能夠簡寫爲:(Int, Int)
。在Scala中,代碼段的最後一句將作爲函數返回值,因此這裏不須要顯示的寫return
關鍵字。
而val (add, sub) = calc(5, 1)
一句,是Scala中的抽取功能。它直接把calc
函數返回的一個Tuple2
值賦給了add
他sub
兩個變量。
本篇文章簡單的介紹了Scala的語言特性,本文並不僅限於Python程序員,任何有編程經驗的程序員均可以看。如今你應該對Scala有了一個基礎的認識,並能夠寫一些簡單的代碼了。以後會分享一些《Scala實戰(系列)》,介紹更函數式的寫法及與實際工程中結合的例子。