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

標籤

2016年4月27日 星期三

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

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

Array : 
在 Scala 中,如果要建立陣列,可以使用 Array 類別,建立陣列時,須指定陣列後存放的元素型態. 如以下建立長度為 5,內含Int、Double、Boolean、String等指定型態的陣列 : 
scala> val arr1 = new Array[Int](5)
arr1: Array[Int] = Array(0, 0, 0, 0, 0)

scala> val arr2 = new Array[Double](5)
arr2: Array[Double] = Array(0.0, 0.0, 0.0, 0.0, 0.0)

scala> val arr2 = new Array[Char](5)
arr2: Array[Char] = Array(?, ?, ?, ?, ?)

scala> val arr2 = new Array[Boolean](5)
arr2: Array[Boolean] = Array(false, false, false, false, false)

scala> val arr2 = new Array[String](5)
arr2: Array[String] = Array(null, null, null, null, null)


在建立陣列之後,如果內含元素是整數,則預設值為 0,浮點數則預設值為 0.0,字元則預設值為空字元(字元編碼為0的字元),布林值預設為 false,其它型態則預設值為 null(與 Java 的陣列類似). 如果要多維陣列,基本上是以一維陣列來摸擬,例如要建立二維陣列 : 
scala> val arr=new Array[Array[Int]](5,5)
warning: there were deprecation warnings; re-run with -deprecation for details
arr: Array[Array[Int]] = Array(Array(0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0), Array(0, 0, 0, 0, 0))

如果要存取陣列,必須指定陣列索引,與其它語言存取陣列慣例不同的是,在 Scala 中,指定陣列索引時是使用 () 而非 [],例如 : 
- Array1.scala 代碼 :
  1. val arr = new Array[Int](3)  
  2. arr(0) = 10  
  3. arr(1) = 20  
  4. arr(2) = 30  
  5. println(arr(0)) // 顯示 10  
  6. println(arr(1)) // 顯示 20  
  7. println(arr(2)) // 顯示 30  

事實上,當你使用 arr(0) = 10 這樣的指定方式時,Scala 會將之轉換為呼叫陣列的 update() 方法,而你用 arr(0) 的方式要取得指定索引的元素時,Scala 會將之轉換為呼叫 apply 方法。以下是個簡單的驗證方式 : 
- Array2.scala 代碼 :
  1. val arr = new Array[Int](3)  
  2. arr.update(010)  
  3. arr.update(120)  
  4. arr.update(230)  
  5. println(arr.apply(0)) // 顯示 10  
  6. println(arr.apply(1)) // 顯示 20  
  7. println(arr.apply(2)) // 顯示 30  

在 Scala 中,指定索引進行存取時使用 () 而不是 [],其實是為了語法的一致性。這種在 () 中指定索引進行存取的方式,並非只有陣列專屬,只要是有提供 update() 或 apply() 方法的物件,其實都可以使用這樣的存取方式,像是在 String 也曾經使用過這樣的方式 : 
  1. val str = "Scala"  
  2. println(str(0)) // 顯示 'S'   
  3. println(str(1)) // 顯示 'c'   
  4. println(str(2)) // 顯示 'a'   
  5. println(str(3)) // 顯示 'l'   
  6. println(str(4)) // 顯示 'a'  
如果你要在建立陣列時,一併指定元素值,則可以直接使用以下的語法 : 
- Array3.scala 代碼 :
  1. val arr = Array(102030)  
  2. for(i <- nbsp="" span="">0 until arr.length) {  
  3.     println(arr(i))  
  4. }  

其實在 單例物件 中有介紹過了,Array(10, 20, 30) 這樣的寫法是個語法蜜糖,Scala 會將之轉換為呼叫 scala.Array 伴侶物件的 apply 方法,這個方法會傳回含指定元素的陣列。以下這個程式可以驗證這個說法 : 
- Array4.scala 代碼 :
  1. val arr = Array.apply(102030)  
  2. for(i <- arr="" nbsp="" span="">
  3.     println(i)  
  4. }  

上面這個範例程式,同時示範了迭代陣列的另一種方式,每一次迭代,都會將 arr 的元素取出指定給i。事實上,陣列提供 foreach 方法可以使用,上面的程式你可以這麼撰寫 : 
- Array5.scala 代碼 :
  1. val arr = Array(102030)  
  2. arr.foreach((i: Int) => println(i))  

在 簡單的函式 中有介紹過,foreach中是函式字面寫法,執行時期會傳入函式物件,每次取出一個元素,就會呼叫函式物件。你可以利用類型推斷,讓這個範例更簡短一些 : 
- Array6.scala 代碼 :
  1. val arr = Array(102030)  
  2. arr.foreach(i => println(i))  

arr.foreach(i => println(i)) 其實是 arr.foreach((i) => println(i)) 簡寫,因為只有一個參數,所以括號可以省略。事實上,就上面這個例子來說,最簡短的寫法是 : 
- Array7.scala 代碼 :
  1. val arr = Array(102030)  
  2. arr.foreach(println)  

事實上,foreach 中是 部份套用函式(Partially applied function)的寫法,這之後還會詳述. 像 foreach 這種傳入函式物件執行某個操作的作法,在 Scala 相當常見。例如陣列的 filter 方法,可以傳入一個會運算出布林值的函式物件,根據真或假決定結果要包括哪些元素,例如 : 
- Array8.scala 代碼 :
  1. val arr = Array(102030405060)  
  2. arr.filter(x => x > 30).foreach(println)  

事實上,上面這個程式可以使用佔位字元語法(Placeholder syntax)撰寫為以下的方式 : 
- Array9.scala 代碼 :
  1. val arr = Array(102030405060)  
  2. arr.filter(_ > 30).foreach(println) // 逐行顯示 40、50、60  

_ 作為佔位字元,就目前來說,你可以將看它看作是填充欄位,每一次取得的元素值將值入這個欄位其實完整的語法應該是 (_ : Int) > 30,不過由於利用了類型推斷,所以可以推斷出_應該是 Int,因此可以寫為 _ > 30,佔位字元語言之後還會詳述). 
類似地,如果你不僅是想過濾,還想對元素作某些運算後傳回的話,則可以使用 map 方法,例如將每個元素加 1 後傳回新陣列 : 
- Array10.scala 代碼 :
  1. val arr = Array(102030)  
  2. arr.map(_ + 1).foreach(println) // 逐行顯示 11、21、31  

陣列可以串接,不過不是使用 +,而是使用 ++這是 Array 上定義的方法),這會將兩個陣列的元素取出,建立一個新陣列後傳回。例如 : 
scala> val arr1 = Array(10, 20, 30)
arr1: Array[Int] = Array(10, 20, 30)

scala> val arr2 = Array(40, 50, 60)
arr2: Array[Int] = Array(40, 50, 60)

scala> val arr3 = (arr1 ++ arr2)
arr3: Array[Int] = Array(10, 20, 30, 40, 50, 60) 
scala> arr3.foreach(x => print(x + " ")); println()
10 20 30 40 50 60

如果你要比較兩個陣列的內含值是否相同,由於要逐一比對陣列中每個陣列的元素物件內含值,所以不能直接使用 == 比較,而要使用 deepEquals 方法。例如 : 
scala> val arr1 = Array(10, 20, 30)
arr1: Array[Int] = Array(10, 20, 30)

scala> val arr2 = Array(10, 20, 30)
arr2: Array[Int] = Array(10, 20, 30)

scala> arr1 == arr2
res1: Boolean = false

scala> arr1 deepEquals arr2
warning: there were 1 deprecation warnings; re-run with -deprecation for details
res2: Boolean = true


scala> arr1.deep
res3: IndexedSeq[Any] = Array(10, 20, 30)

scala> arr2.deep
res4: IndexedSeq[Any] = Array(10, 20, 30)

scala> arr1.deep.equals(arr2.deep)
res5: Boolean = true

Supplement 
Tutorialspoint - Scala Arrays

沒有留言:

張貼留言

網誌存檔

關於我自己

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