教程 | 寫給Python程序員的Scala入門教程

統計網導讀 本文從安裝、基礎數據類型、運算符、函數等七個方面系統介紹了Scala的語言特性。不僅是Python程序員,任何有編程經驗或者希望入門的人都可以看看,對Scala做一個基礎的認識。

隨着業務和數據的需要,我們引入了SparkSparkPython的支持還是挺好的,但畢竟它還是使用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
2
3
4
5
6
7
8
9
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>提示符,這個就像PythonREPL一樣,Scala也擁有一個REPL。我們可以在這裏很方便的編寫一些代碼,測試一些想法。

對於Scala常用的IDE(集成開發環境),推薦使用IDEA for scala pluginsscala-ide

Scala的強大,除了它自身對多核編程更好的支持、函數式特性及一些基於Scala的第3方庫和框架(如:Akka、Playframework、Spark、Kafka……),還在於它可以無縫與Java結合。所有爲Java開發的庫、框架都可以自然的融入Scala環境。當然,Scala也可以很方便的Java環境集成,比如:Spring。若你需要第3方庫的支持,可以使用MavenGradleSbt等編譯環境來引入。

Scala是一個面向對象的函數式特性編程語言,它繼承了Java的面向對特性,同時又從Haskell那裏吸收了很多函數式特性並做了增強。

2Hello, world.

通常情況下,Java系的程序都需要有一個main方法來執行。並需要放入一個Web container容器,或打成一個jar包來執行。但是Scala不一樣,它除了支持傳統的Java方式,它也可以像Python一樣把代碼保存到一個腳本文件裏(.scala)執行,就像一個Shell腳本一樣。


1
2
3
4
5
6
7
8
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一樣通過代碼縮進來表示代碼的層次關係,而是和通常的語言一樣使用{}來表示代碼的層級。給程序員更多的自由)

3變量、基礎數據類型

Scala中變量不需要顯示指定類型,但需要提前聲明。這可以避免很多命名空間污染問題。Scala有一個很強大的類型自動推導功能,它可以根據右值及上下文自動推導出變量的類型。你可以通過如下方式來直接聲明並賦值。


1
2
3
4
5
6
7
8
9
10
11
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中沒在區分原生類型和裝箱類型,如:intInteger。它統一抽象成Int類型,這樣在Scala中所有類型都是對象了。編譯器在編譯時將自動決定使用原生類型還是裝箱類型。

字符串

Scala中單引號和雙引號包裹是有區別的,單引號用於字符,雙引號用於字符串。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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中也有特殊含義,它代表被包裹的內容是原始字符串,可以不需要字符轉碼。這一特性在定義正則表達式時很有優勢。

4運算符

Scala中的運算符其實是定義在對象上的方法(函數),你看到的諸如:3 + 2其實是這樣子的:3.+(2)+符號是定義在Int對象上的一個方法。支持和Java一至的運算符(方法):

(注:在Scala中,方法前的.號和方法兩邊的小括號在不引起歧義的情況下是可以省略的。這樣我們就可以定義出很優美的DSL

  • ==!=:比較運算

  • !|&^:邏輯運算

  • >><<:位運算

在Scala中,修正(算更符合一般人的常規理解吧)==!=運算符的含義。在Scala中,==!=是執行對象的值比較,相當於Java中的equals方法(實際上編譯器在編譯時也是這麼做的)。而對象比較需要使用eqne兩個方法來實現。

5控制語句

Scala中支持ifwhilefor comprehension(for表達式)、match case(模式匹配)四大主要控制語句。Scala不支持switch? :兩種控制語句,但它的ifmatch case會有更好的實現。

if

Scala支持if語句,其基本使用和JavaPython中的一樣。但不同的時,它是有返回值的。

(注:Scala是函數式語言,函數式語言還有一大特性就是:表達式。函數式語言中所有語句都是基於“表達式”的,而“表達式”的一個特性就是它會有一個值。所有像Java中的? :3目運算符可以使用if語句來代替)。


1
2
3
4
5
6
7
8
9
10
11
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語句也是有返回值的,將表達式的結果賦給變量,編譯器也能正常推導出變量的類型。unitunit2變量的類型是Any,這是因爲else語句的缺失,Scala編譯器就按最大化類型來推導,而Any類型是Scala中的根類型。()在Scala中是Unit類型的實例,可以看做是Java中的Void

while

Scala中的while循環語句:


1
2
3
while (條件) {
 語句塊
}


for comprehension

Scala中也有for表達式,但它和Java以及Python中的for不太一樣,它具有更強大的特性。通常的for語句如下:


1
2
3
for (變量 <- 集合) {
 語句塊
}


Scala中for表達式除了上面那樣的常規用法,它還可以使用yield關鍵字將集合映射爲另一個集合:


1
2
3
4
5
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判斷:


1
2
scala> val list3 = for (item <- list if item % 2 == 0) yield item
list3: List[Int] = List(2, 4)


還可以做flatMap操作,解析2維列表並將結果攤平(將2維列表拉平爲一維列表):


1
2
3
4
5
6
7
8
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中,而是整個集合庫都支持這一特性,包括:SeqMapSetArray……

Scala和Python一樣,也沒有C-Like語言裏的for (int i = 0; i < 10; i++)語法,但和Python類似的它也有xrangerange函數樣的效果。在Scala中的使用方式如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
scala> for (i <- (0 until 10)) {
    |   println(i)
    | }
0
1
2
3
4
5
6
7
8
9


比Python更好的一點時,Scala中還有一個to方法(Scala中去處符其實都是定義在對象上的方法/函數):


1
2
scala> for (i <- (0 to 10)) print(" " + i)
0 1 2 3 4 5 6 7 8 9 10


match case

模式匹配,是函數式語言很強大的一個特性。它比命令式語言裏的switch更好用,表達性更強。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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中的模式匹配還有類型匹配、數據抽取、謂詞判斷等其它有用的功能。這裏只做簡單介紹,之後會單獨一個章節來做較詳細的解讀。

6集合

在Python中,常用的集合類型有:listtuplesetdict。Scala中對應的有:ListTuple[X]SetMap

List

Scala中List是一個遞歸不可變集合,它很精妙的使用遞歸結構定義了一個列表集合。除了之前使用Listobject來定義一個列表,還可以使用如下方式:


1
2
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有很多常用的方法:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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中將其範化到所以序列集合上(SeqListSetArray……)都可以支持。

Tuple

Scala中也支持Tuple(元組)這種集合,但最多隻支持22個元素(事實上Scala中定義了Tuple0Tuple1……Tuple22這樣22個TupleX類,實現方式與C++ Boost庫中的Tuple類似)。和Python中類似,Scala也採用小括號來定義元組。


1
2
3
4
5
6
7
8
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對象:


1
2
3
4
5
6
7
8
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來定義:


1
2
3
4
5
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時,傳入的每個EntryKV對)其實就是一個Tuple2(有兩個元素的元組),而->是定義Tuple2的一種便捷方式。


1
2
3
4
5
6
7
8
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中變量默認都是不可變的),推薦還是採用不可變集合。因爲它更直觀、線程安全,你可以確定你的變量不會在其它地方被不小心的更改。)

7函數(初級)

在Scala中,函數是一等公民。函數可以像類型一樣被賦值給一個變量,也可以做爲一個函數的參數被傳入,甚至還可以做爲函數的返回值返回(這就是函數式編程)。

他Python一樣,Scala也使用def關鍵詞來定義一個函數:


1
2
3
4
5
6
7
8
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,它有兩個參數:n1n2,其類型爲:Intcala函數的返回值類型是一個有兩個元素的元組,在Scala中可以簡寫爲:(Int, Int)。在Scala中,代碼段的最後一句將做爲函數返回值,所以這裏不需要顯示的寫return關鍵字。

val (add, sub) = calc(5, 1)一句,是Scala中的抽取功能。它直接把calc函數返回的一個Tuple2值賦給了addsub兩個變量。

8總結


本篇文章簡單的介紹了Scala的語言特性,本文並不只限於Python程序員,任何有編程經驗的程序員都可以看。現在你應該對Scala有了一個基礎的認識,並可以寫一些簡單的代碼了。之後會分享一些《Scala實戰(系列)》,介紹更函數式的寫法及與實際工程中結合的例子。


作者:楊晨 

來源:SOTON數據分析


轉自:http://wwwbuild.net/cntongji/376918.html

發佈了28 篇原創文章 · 獲贊 32 · 訪問量 30萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章