程式扎記: [Scala 小學堂] Scala Gossic : 起步走 - 常用物件 (List 與 Tuple)

標籤

2016年5月3日 星期二

[Scala 小學堂] Scala Gossic : 起步走 - 常用物件 (List 與 Tuple)

轉載自 這裡 
前言 : 
Scala 是一個可直譯、編譯、靜態、運行於 JVM 之上、可與 Java 互操作、融合物件導向編程特性與函式編程風格的程式語言. 從一些簡單的語法開始,了解 Scala 的許多特性,所以就算是起步走,東西也夠多了. 簡單的小程式可以使用 scala 指令,編譯的事就交給 scalac,編譯想快點就用 fsc. (Scala 官網), 在變與不變之間,先體會 Scala 更多特性. 

List 與 Tuple : 
scala.collection.immutable.List 是有序的物件群集(Collection),與 陣列或 Java 的 List)不同的是,在 Scala 中,List 是不可變動的Immutable),這表示你不可以改變物件的內容或狀態,就如同 字串 一樣. Scala 鼓勵你使用或設計不可變動的物件,因為不可變動物件不會有複雜的狀態變化,你不用擔心物件在傳遞的過程中,不小心被改變了狀態,在多執行緒環境下,也不用擔心共用存取的競速Race condition問題. 在 Scala 中要建立 List,必須使用工廠方法 : 
- List1.scala 代碼 :
  1. val list = List(102030)  
  2. for(i <- nbsp="" span="">0 until list.length) {  
  3.         println(list(i))  
  4. }  

上例中,建立了一個 List,內含元素10、20、30,要取得元素同樣是指定索引,索引可使用 () 的方式來指定,就如同陣列索引的指定方式,List 是不可變動的,所以你無法使用 list(0) = 20 這樣的方式來改變 List 的元素內容. List 是抽象類別,你沒辦法直接用來建立實例,如果你要建立一個 List 物件,沒有任何的元素的話,可以使用 List() 或者是寫下 NilNil 是List[Nothing]的實例)。List 的 == 被定義為可以比較兩個 List 的元素是否相同,以下是個範例 : 
- List2.scala 代碼 :
  1. val list1 = List(102030)  
  2. val list2 = List(102030)  
  3. val list3 = List(203040)  
  4. println(list1 == list2)  // true  
  5. println(list1 == list3)  // false  
  6. println(List() == Nil)   // true  

如果要在 List 中附加元素,可以使用 :: 方法,不過要注意,:: 是將元素附加至 List 的前端,由於 List 是不可變動的,所以傳回的是新建立的 List 物件,例如 : 
- List3.scala 代碼 :
  1. val list1 = List(102030)  
  2. (5::list1).foreach(println)   //Show 5, 10, 20, 30  
  3. val list2 = 1 :: 2 :: 3 :: 4 :: Nil  
  4. list2.foreach(println)        //Show 1, 2, 3, 4  

上 例中,還示範了如何從空 List 逐步附加元素。Scala 沒有提供將元素附加至 List 後端的方法(其實本來是有 + 方法,不過已經被標示為 deprecation,也就是廢棄不建議使用),將元素附加至 List 前端很快速,只需要常數時間(Constant time),但將元素附加至List 後端,則會隨著 List 的長度而增加線性時間. 在 操作順序與關聯 中有提過,如果運算方法最後一個字元是 :,其實該方法是屬於右邊物件,所以 5 :: list1,其實是 list1.::(5),例如 : 
- List4.scala 代碼 :
  1. val list1 = List(102030)  
  2. list1.::(5).foreach(println)   //Show 5, 10, 20, 30  

如果你想串接兩個 List,則可以使用 ::: 方法,例如 : 
- List5.scala 代碼 :
  1. val list1 = List(102030)  
  2. val list2 = List(405060)  
  3. (list1 ::: list2).foreach(println)   //Show 10, 20, 30, 40, 50, 60  
  4. list2.:::(list1).foreach(println)    //Show 10, 20, 30, 40, 50, 60  

::: 同樣是以: 結尾,所以是由右邊物件呼叫的方法,在上面的範例也有示範過了。如果你要將一個 List 附加至另一個 List 之後,其實也可以使用 ++ 方法,例如 : 
- List6.scala 代碼 :
  1. val list1 = List(102030)  
  2. val list2 = List(405060)  
  3. (list1 ++ list2).foreach(println)    // Show 10, 20, 30, 40, 50, 60  

使用 ++ 似乎與使用 ::: 的作用相同,不過有所不同的是,++ 是由左邊物件呼叫的方法,而 ++ 右邊的物件可以是 scala.Iterable 物件,而不用得是 List,例如,你可以將陣列的元素內容附加至 List 之後 : 
- List7.scala 代碼 :
  1. val list = List(102030)  
  2. val arr = Array(405060)  
  3. (list ++ arr).foreach(println)    // Show 10, 20, 30, 40, 50, 60  

如果你忽略 : 結尾的方法其實是由右邊物件呼叫,那麼有可能會發生像以下的錯誤 : 
val list = List(10, 20, 30)
val arr = Array(40, 50, 60)
val result = (list ::: arr)
 // 這是錯的


你可以使用 - 從 List 中刪去指定的元素,注意所有符合的元素都會被刪掉 : 
val list = List(10, 10, 20, 20, 30, 30)
(list - 20).foreach(println)
 // 逐行顯示 10、10、30、30

使用 -- 的話,則可以刪去另一個 List 物件中所擁有的元素,例如 : 
val list1 = List(10, 20, 30, 40, 50)
val list2 = List(20, 30)
(list1 -- list2).foreach(println)
 // 逐行顯示 10、40、50

List 中的元素必須是相同的類型,如果你想要在某個群集物件中放置不同型態的物件,而且又要群集物件記得每個元素的型態,則可以使用 Tuple(List 中如果放置不同型態的物件,則 List 會全部視為 scala.Any,這是 Scala 所有類別的最頂層父類別,每個物件的型態資訊基本上就失去了,你必須自己記得放了什麼)。Tuple 同樣是不可變動的。一個例子如下所示 : 
scala> val tuple = (10, "John", true)
tuple: (Int, java.lang.String, Boolean) = (10,John,true)

scala> tuple.getClass()
res3: java.lang.Class[_ <: boolean="" class="" font="" java.lang.string="" nt="" scala.tuple3="">

scala> tuple._1
res4: Int = 10

scala> tuple._2
res5: java.lang.String = John

scala> tuple._3
res6: Boolean = true

注意到,存取元素時是使用 _n 的方式,而 n 是從1開始這是來自其它有靜態型別 tuple 特性的語言傳統,如 Haskell),由於 Tuple 中每個元素的型態資訊不同,因此無法僅靠像 List 或陣列的索引存取方式來記得型態資訊,因而給予每個元素個別的 _n 名稱. 在建立 Tuple 時,可以直接在 () 中指定元素,不過要注意的是,如果你的 Tuple 中只有一個元素,則必須使用 Tuple() 來定義,例如 : 
val tuple = Tuple(1)

上例中,Tuple 其實是 scala.Predef 中定義的名稱,如果你沒有使用上例的方式,而是直接用 (1),則會被視為定義一個整數,而非 Tuple。Tuple 的實際型態是根據它所包括的元素個數來決定(包括參數化類型資訊),例如 : 
- List11.scala 代碼 :
  1. val tuple1 = (10"John")  
  2. val tuple2 = (10"John"true)  
  3. println(tuple1.getClass)  // Show class scala.Tuple2  
  4. println(tuple2.getClass)  // Show class scala.Tuple3  

上例中,tuple1 的參數化類型為 Tuple2[Int, String],tuple2 則為Tuple3[Int, String, Boolean]。Tuple 最大的用途之一,就是可以同時指定多個變數,例如 : 
- List12.scala 代碼 :
  1. val (id, name) = (123"Justin")  
  2. println(id)           // Show 123  
  3. println(name)         // Show john  

上例中,實際上宣告了兩個變數 id 與 name,並被分別指定 Tuple 物件中對應的值。當你要從函式傳回兩個以上的物件時,這個特性就很有用 : 
- List13.scala 代碼 :
  1. def info(data: String) = {  
  2.     // Do some thing  
  3.     val id = 123           // Get id  
  4.     val name = "John"    // Get name  
  5.     (id, name)  
  6. }  
  7.   
  8. val (id, name) = info("Please input:")  
  9. println(id)          //Show 123  
  10. println(name)        //Show John  

Supplement 
Tutorialspoint - Scala Collection

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!