程式邏輯的控制流程, 大部分都是從if else 起手, 可是當遇到複雜的資料結構要分析, 用底層的簡單判斷式就有點捉襟見肘, 複雜的流程設計可以透過switch來建置, 而Pattern match又更上一層樓的可以處理各種使用者自定義的資料結構, 實際上這也是來自於Functional programming的設計
public class Ordinal { public static void main(String[] args) { ordinal(Integer.parseInt(args[0])); } public static void ordinal(int number) { switch(number) { case 1: System.out.println("1st"); break; case 2: System.out.println("2nd"); break; case 3: System.out.println("3rd"); break; case 4: System.out.println("4th"); break; case 5: System.out.println("5th"); break; case 6: System.out.println("6th"); break; case 7: System.out.println("7th"); break; case 8: System.out.println("8th"); break; case 9: System.out.println("9th"); break; case 10: System.out.println("10th"); break; default : System.out.println("Cannot do beyond 10"); } } }Scala 版的Pattern match 去找出輸入的數字次序
public class Ordinal { ordinal(args(0).toInt) def ordinal(number:Int) = number match { case 1 => println("1st") case 2 => println("2nd") case 3 => println("3rd") case 4 => println("4th") case 5 => println("5th") case 6 => println("6th") case 7 => println("7th") case 8 => println("8th") case 9 => println("9th") case 10 => println("10th") case _ => println("Cannot do beyond 10") }基本流程上都一樣, 取得來自command line的 args 字串並轉換成數字,來做比對看看該數字的次序, 在Scala 版的Pattern match 除了 match Keyword 外還有幾個一眼可觀察到的語法差異
- 每個case結尾不必加上break, Scala Pattern match不像JAVA switch沒有break的話會執行到後續的case
- default case在 Scala Pattern match中用 底線 _ 取代, 底線 _ 在Scala中有很多種用法, 這邊又是一點, 這樣用法很類似宣告變數的default value 用法
錯誤處理
當pattern match找不到相對應的case時候, Scala compiler會出現match error, 如下2 match { case 1 => "One" } scala.MatchError: 2 at .<init>(<console>:5) at .<clinit>(<console>)當你的要比對的對象沒有符合的case, 就會出現match error, 在JAVA的儘管刪去default case且比對不到, JAVA還是繼續執行下去且沒有回傳任何東西, 相較之下, Scala的作法會比較安全的多
更複雜的資料結構比對
在JAVA的 switch 中只能處理primitive type與enums, 但Scala Pattern match進化到可以比對字串, 資料結構, 類型, 變數, 常數 以及建構子等複雜的資料結構,Type match
def printType(obj: AnyRef) = obj match { case s: String => println("This is string") case l: List[_] => println("This is List") case a: Array[_] => println("This is an array") case d: java.util.Date => println("This is a date") }在上例中, printType 接收一個物件為參數, 並判斷出它的類型是甚麼, 透過 obj 原本的類型來找出相對應的case, 當一但比對到, obj 的值就會綁定到該類型case中的變數, 比如 obj 原本是字串類型, 那麼會對應到 case s : String, 並且把obj 的值綁定到變數 s 上, 在case的 => 後面的處理程序如果需要用到obj 就call s變數就可以了, 儘管這樣的類型比對在JAVA透過instanceof 與 casting的操作也可以達到, 但很顯然的Scala 版本簡潔的多,
BTW, AnyRef 物件是Scala中所有自定義物件的次祖先(等同JAVA中的 Object), 它再上去就是祖先Any 物件, 是所有物件的祖先, 關於Scala的Class hierarchy 在後續會繼續說明
Infix pattern match
在Scala Pattern match中也可以操做Infix(中序)的Pattern, 在 Infix(中序)結構中, 運算值會放在運算符號的左右兩邊, 就如同 2 + 2; +是運算符號, 2是運算值, Infix(中序)結構最常見的就是List pattern, 如下面範例List(1, 2, 3, 4) match { case f :: s :: rest => List(f, s) case _ => Nil } //return List[Int] = List(1, 2):: (兩個冒號)method 可以用來串接List, 因此 f :: s 就是一個Infix(中序)結構
由運算結構得知 :: 是中序運算符號, f & s 是運算值, f :: s 就是把f & s串成一個List
而整個Pattern f :: s :: rest 就是把整個 List(1, 2, 3, 4) 拆成第一個元素f , 加上第二個元素s , 加上剩下的成為一個 rest List (要注意f or s 各別都是Any類型), 因此 List(1, 2, 3, 4)就會變成 1 :: 2 :: List( 3,4 ), 其中 f = 1 , s = 2, rest = List( 3,4), 另一個理解方式就是反過來思考 List(1, 2, 3, 4)就是由 rest 串列(List(3, 4))透過 :: method去加上 f = 1 & s = 2 這兩個元素所組成的
更細節的Pattern match應用, 會在Case class時候進一步說明
待續
沒有留言:
張貼留言