隨着業務和數據的需要,我們引入了Spark
。Spark
對Python
的支持還是挺好的,但畢竟它還是使用Scala
開發的,且現有的API並沒有100%覆蓋Python。所以就有了這篇文章,讓Python程序員可以接觸Scala
這門更高(級)、更快(速)、更強(大)的(奧運精神)語言。
Scala兼具Python樣的開發效率,但又有Java般的執行性能,真是一不可多得的神器!(當然,魚和熊不可兼得,Scala的入門曲線相比Python是要那麼陡峭一丟丟)
1安裝
一般Linux系統都自帶Python
環境,但Scala是沒有的。這需要我們手動安裝,還需要安裝Java環境。Java環境的安裝這裏就不介紹了,網上很多。說說Scala的安裝吧。下載地址在http://scala-lang.org/download/2.11.7.html。
1
|
wget -c http://downloads.typesafe.com/scala/2.11.7/scala-2.11.7.tgz |
我們可以看到終端出現了scala>
提示符,這個就像Python
的REPL
一樣,Scala也擁有一個REPL
。我們可以在這裏很方便的編寫一些代碼,測試一些想法。
對於Scala常用的IDE(集成開發環境),推薦使用IDEA for scala plugins和scala-ide。
Scala的強大,除了它自身對多核編程更好的支持、函數式特性及一些基於Scala的第3方庫和框架(如:Akka、Playframework、Spark、Kafka……),還在於它可以無縫與Java結合。所有爲Java開發的庫、框架都可以自然的融入Scala環境。當然,Scala也可以很方便的Java環境集成,比如:Spring。若你需要第3方庫的支持,可以使用Maven、Gradle、Sbt等編譯環境來引入。
Scala是一個面向對象的函數式特性編程語言,它繼承了Java的面向對特性,同時又從Haskell
那裏吸收了很多函數式特性並做了增強。
2Hello, world.
通常情況下,Java系的程序都需要有一個main方法來執行。並需要放入一個Web
container
容器,或打成一個jar
包來執行。但是Scala不一樣,它除了支持傳統的Java方式,它也可以像Python一樣把代碼保存到一個腳本文件裏(.scala)執行,就像一個Shell腳本一樣。
1
|
yangjing-mac-air:scala-2.11.7 jingyang$ cat test.scala |
(注:需要把$SCALA_HOME/bin
加入系統環境變量才能直接執行scala
命令)
可以看到,使用Scala,你除了像傳統的Java程序一樣把它做爲一個“服務”的方式來啓動,你也可以把它像Python一樣做爲一個“腳本”來啓動。
(注意:Scala不像Python一樣通過代碼縮進來表示代碼的層次關係,而是和通常的語言一樣使用{}
來表示代碼的層級。給程序員更多的自由)
3變量、基礎數據類型
Scala中變量不需要顯示指定類型,但需要提前聲明。這可以避免很多命名空間污染問題。Scala有一個很強大的類型自動推導功能,它可以根據右值及上下文自動推導出變量的類型。你可以通過如下方式來直接聲明並賦值。
1
|
scala> val a = 1 |
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中單引號和雙引號包裹是有區別的,單引號用於字符,雙引號用於字符串。
1
|
scala> val c1 = 'c' |
Scala基於JVM平臺,默認使用unicode,所以變量名是可以直接用中文的。而在Scala中,中文也是直接顯示的,不像Python2一樣會輸出成unicdoe編碼形式:\uxxxx。
Scala還支持String
Interpolation(“字符串插值”)的特性,可以使用${variable
name}
這樣的形式引用變量,並將值插入。就像示例s2
一樣。
而連線3個雙引號在Scala中也有特殊含義,它代表被包裹的內容是原始字符串,可以不需要字符轉碼。這一特性在定義正則表達式時很有優勢。
4運算符
Scala中的運算符其實是定義在對象上的方法(函數),你看到的諸如:3
+ 2
其實是這樣子的:3.+(2)
。+
符號是定義在Int
對象上的一個方法。支持和Java一至的運算符(方法):
(注:在Scala中,方法前的.
號和方法兩邊的小括號在不引起歧義的情況下是可以省略的。這樣我們就可以定義出很優美的DSL
)
-
==
、!=
:比較運算 -
!
、|
、&
、^
:邏輯運算 -
>>
、<<
:位運算
在Scala中,修正(算更符合一般人的常規理解吧)==
和!=
運算符的含義。在Scala中,==
和!=
是執行對象的值比較,相當於Java中的equals
方法(實際上編譯器在編譯時也是這麼做的)。而對象比較需要使用eq
和ne
兩個方法來實現。
5控制語句
Scala中支持if
、while
、for
comprehension
(for表達式)、match
case
(模式匹配)四大主要控制語句。Scala不支持switch
和?
:
兩種控制語句,但它的if
和match
case
會有更好的實現。
if
Scala支持if
語句,其基本使用和Java
、Python
中的一樣。但不同的時,它是有返回值的。
(注:Scala是函數式語言,函數式語言還有一大特性就是:表達式。函數式語言中所有語句都是基於“表達式”的,而“表達式”的一個特性就是它會有一個值。所有像Java
中的?
:
3目運算符可以使用if
語句來代替)。
1
|
scala> if (true) "真" else "假" |
可以看到,if
語句也是有返回值的,將表達式的結果賦給變量,編譯器也能正常推導出變量的類型。unit
和unit2
變量的類型是Any
,這是因爲else
語句的缺失,Scala編譯器就按最大化類型來推導,而Any
類型是Scala中的根類型。()
在Scala中是Unit
類型的實例,可以看做是Java
中的Void
。
while
Scala中的while
循環語句:
1
|
while (條件) { |
for
comprehension
Scala中也有for
表達式,但它和Java
以及Python
中的for
不太一樣,它具有更強大的特性。通常的for
語句如下:
1
|
for (變量 <- 集合) { |
Scala中for
表達式除了上面那樣的常規用法,它還可以使用yield
關鍵字將集合映射爲另一個集合:
1
|
scala> val list = List(1, 2, 3, 4, 5) |
還可以在表達式中使用if
判斷:
1
|
scala> val list3 = for (item <- list if item % 2 == 0) yield item |
還可以做flatMap
操作,解析2維列表並將結果攤平(將2維列表拉平爲一維列表):
1
|
scala> val llist = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9)) |
看到了,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中的使用方式如下:
1
|
scala> for (i <- (0 until 10)) { |
比Python更好的一點時,Scala中還有一個to
方法(Scala中去處符其實都是定義在對象上的方法/函數):
1
|
scala> for (i <- (0 to 10)) print(" " + i) |
match case
模式匹配,是函數式語言很強大的一個特性。它比命令式語言裏的switch
更好用,表達性更強。
1
|
scala> def level(s: Int) = s match { |
可以看到,模式匹配可以使用switch
相同的功能。但也switch
需要使用break
明確告知終止之後的判斷不同,Scala中的match
case
是默認break的,只要其中一個case
語句匹配,就終止之後的所以比較。且對應case
語句的表達式值將作爲整個match
case
表達式的值返回。
Scala中的模式匹配還有類型匹配、數據抽取、謂詞判斷等其它有用的功能。這裏只做簡單介紹,之後會單獨一個章節來做較詳細的解讀。
6集合
在Python中,常用的集合類型有:list
、tuple
、set
、dict
。Scala中對應的有:List
、Tuple[X]
、Set
、Map
。
List
Scala中List
是一個遞歸不可變集合,它很精妙的使用遞歸結構定義了一個列表集合。除了之前使用List
object來定義一個列表,還可以使用如下方式:
1
|
scala> val list = 1 :: 2 :: 3 :: 4 :: 5 :: Nil |
List
採用前綴操作的方式(所有操作都在列表頂端(開頭))進行,::
操作符的作用是將一個元素和列表連接起來,並把元素放在列表的開頭。這樣List
的操作就可以定義成一個遞歸操作。添加一個元素就是把元素加到列表的開頭,List只需要更改下頭指針,而刪除一個元素就是把List的頭指針指向列表中的第2個元素。這樣,List
的實現就非常的高效,它也不需要對內存做任何的轉移操作。List
有很多常用的方法:
1
|
scala> list.indexOf(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也採用小括號來定義元組。
1
|
scala> val tuple1 = (1, 2, 3) |
可以使用xxx._[X]
的形式來引用Tuple
中某一個具體元素,其_[X]
下標是從1開始的,一直到22(若有定義這麼多)。
Set
Set
是一個不重複且無序的集合,初始化一個Set
需要使用Set
對象:
1
|
scala> val set = Set("Python", "Scala", "Java", "C++", "Javascript", "C#", "PHP") |
Map
Scala中的Map
是一個HashMap,其key
也是無序的。Python中的dict
對應些類型(可以使用TreeMap
來讓Map有序)。與Python中不一樣,Scala並沒有提供一個{}
來定義Map
,它還是很統一的採用相關類型的object來定義:
1
|
scala> val map = Map("a" -> "A", "b" -> "B") |
Scala中定義Map
時,傳入的每個Entry
(K、V對)其實就是一個Tuple2
(有兩個元素的元組),而->
是定義Tuple2
的一種便捷方式。
1
|
scala> map + ("z" -> "Z") |
Scala的immutable collection並沒有添加和刪除元素的操作,其定義+
(List
使用::
在頭部添加)操作都是生成一個新的集合,而要刪除一個元素一般使用.filterNot
函數來映射一個新的集合實現。
(注:Scala中也scala.collection.mutable._
集合,它定義了不可變集合的相應可變集合版本。一般情況下,除非一性性能優先的操作(其實Scala集合採用了共享變量的優化,生成一個新集合並不會生成所有元素的副本,它將會和老的集合共享大元素。因爲Scala中變量默認都是不可變的),推薦還是採用不可變集合。因爲它更直觀、線程安全,你可以確定你的變量不會在其它地方被不小心的更改。)
7函數(初級)
在Scala中,函數是一等公民。函數可以像類型一樣被賦值給一個變量,也可以做爲一個函數的參數被傳入,甚至還可以做爲函數的返回值返回(這就是函數式編程)。
他Python一樣,Scala也使用def
關鍵詞來定義一個函數:
1
|
scala> def calc(n1: Int, n2: Int): (Int, Int) = { |
這裏定義了一個函數:calc
,它有兩個參數:n1
和n2
,其類型爲:Int
。cala
函數的返回值類型是一個有兩個元素的元組,在Scala中可以簡寫爲:(Int,
Int)
。在Scala中,代碼段的最後一句將做爲函數返回值,所以這裏不需要顯示的寫return
關鍵字。
而val
(add, sub) = calc(5, 1)
一句,是Scala中的抽取功能。它直接把calc
函數返回的一個Tuple2
值賦給了add
他sub
兩個變量。
8總結
本篇文章簡單的介紹了Scala的語言特性,本文並不只限於Python程序員,任何有編程經驗的程序員都可以看。現在你應該對Scala有了一個基礎的認識,並可以寫一些簡單的代碼了。之後會分享一些《Scala實戰(系列)》,介紹更函數式的寫法及與實際工程中結合的例子。
作者:楊晨
來源:SOTON數據分析
轉自:http://wwwbuild.net/cntongji/376918.html