Source From Here
基本模式
基本模式可以單獨運用,也可以彼此組合,以形成更複雜的模式。這將將介紹的基本模式包括了:
模式中最簡單的種類是 常數模式(Constant pattern),你可以在 Scala 中寫下的字面常量(Literal)都可以作為模式比對,例如:
使用 match 運算式時,如果無法比對成功,會丟出
MatchError。你可以在最後的 case 放一個 _,這表示符合任何對象,這是 萬用字元模式(Wildcard pattern)的一個應用。以下也是個 萬用字元模式 的運用,你只想知道傳入的是 Point 或不是 Point:
實際上,上面先使用
建構式模式(Constructor pattern),看看傳入的物件是不是 Point 所建構,如果是的話,再進一步來到了 萬用字元模式,所以不在乎 x 或 y 值為何。接著來看看 變數模式(Variable pattern)運用的一個例子:
在不是 100 或 90 的情況下,則符合最後的 case,而且會將比對的物件指定給 something 這個變數。這個例子看不出
變數模式 的實際運用,來看看這個例子:
上面先使用
建構式模式(Constructor pattern),看看傳入的物件是不是 Point 所建構,如果是的話,再進一步將 Point 中的值分別指定給 x 與 y 變數,在 => 之中就可以直接取用 x 與 y 的值。在使用 常數模式 時,需注意別與 變數模式 混淆,例如你也許以為下面這個程式是 常數模式 比對:
但事實是,你使用了
變數模式,x 是 match 中的一個變數,而不是你在第一行所宣告的 x,上面的程式會有編譯警告:
變數模式 一定會先匹配到,所以之後的 萬用字元模式 永遠不會被匹配到。在 Scala 中,一個常數在命名時,首字母必須大寫,這不僅是慣例,也是在某些場合被認定為常數的要件。例如以下的程式就可得到預期結果:
變數 X 是首字大寫,在 match 中會被認定為
常數模式,因此可以編譯成功並執行。再繼續來看到 建構式模式,它可以形成巢狀,例如:
上例中使用了
建構式模式 與 萬用字元模式,以傳入 Cylinder 為例,會使用 建構式模式 比對 Cylinder,符合後再使用 建構式模式 比較內層的 Circle,符合後再使用 建構式比較更內層的 Point,最後使用 萬用字元 比對。
再來看到 型別模式(Typed pattern),直接使用 重新定義 equals() 方法 中的一個例子作說明:
在第一個 case 中的比對中,傳入的物件型態必須符合
Point 型別,如果是的話,指定給 that 變數。這個例子如果不使用 型別模式,則你可以這麼撰寫:
一般來說,不鼓勵直接進行型態檢查與型態轉換,寫來也比較冗長,建議還是採用模式匹配的方式。在使用
型別模式 時,若想匹配 List、Set、Map 等型態,可以使用以下的方式:
但是你沒辦法指匹配群集中別元素型態,例如:
這在編譯時會出現警示訊息:
理由在於,Scala 的泛型(Generic)採用的是 型別抹除(Type erasure)的作法,加入群集後的物件基本上就失去型態資訊了(如果你熟悉 Java,這跟物件加入 Java 群集中意思是一樣的,所有的物件失去的型態資訊)。如果你執意運行以下的程式,結果將不正確:
唯一的例外是陣列,陣列沒有採用型別抹除,因為陣列在 Scala 中處理的方式特意與 Java 中陣列相同,所以下面的寫法是可行的:
基本模式
基本模式可以單獨運用,也可以彼此組合,以形成更複雜的模式。這將將介紹的基本模式包括了:
模式中最簡單的種類是 常數模式(Constant pattern),你可以在 Scala 中寫下的字面常量(Literal)都可以作為模式比對,例如:
- def what(a:Any) = a match {
- case 10 => "整數"
- case 0.1 => "浮點數"
- case 'A' => "字元"
- case true => "布林值"
- case "text" => "字串"
- case Nil => "空串列"
- case _ => "?"
- }
- case class Point(x: Int, y: Int)
- def what(a: Any) = a match {
- case Point(_, _) => "圓"
- case _ => "不是圓"
- }
- println(what(Point(1, 2))) // 顯示圓
- println(what(Point(3, 4))) // 顯示圓
- println(what("圓?")) // 顯示不是圓
- def what(i: Any) = i match {
- case 100 => "滿分"
- case 90 => "A"
- case something => "不及格?" + something
- }
- println(what(100)) // 滿分
- println(what(90)) // A
- println(what(80)) // 不及格?80
- case class Point(x: Int, y: Int)
- def what(a: Any) = a match {
- case Point(x, y) => "圓 (" + x + ", " + y + ")"
- case _ => "不是圓"
- }
- println(what(Point(1, 2))) // 圓 (1, 2)
- println(what(Point(3, 4))) // 圓 (3, 4)
- println(what("圓?")) // 不是圓
- val x = 10
- def what(i: Int) = i match {
- case x => "10"
- case _ => "不是 10"
- }
- println(what(10))
- println(what(20))
變數模式 一定會先匹配到,所以之後的 萬用字元模式 永遠不會被匹配到。在 Scala 中,一個常數在命名時,首字母必須大寫,這不僅是慣例,也是在某些場合被認定為常數的要件。例如以下的程式就可得到預期結果:
- val X = 10
- def what(i: Int) = i match {
- case X => "10"
- case _ => "不是 10"
- }
- println(what(10)) // 10
- println(what(20)) // 不是 10
- case class Point(x: Int, y: Int)
- case class Circle(p: Point, r: Int)
- case class Cylinder(c: Circle, h: Int)
- def what(a: Any) = a match {
- case Point(_, _) => "點"
- case Circle(Point(_, _), _) => "圓"
- case Cylinder(Circle(Point(_, _), _), _) => "柱"
- }
- println(what(Point(10, 10))) // 點
- println(what(Circle(Point(10, 10), 10))) // 圓
- println(what(Cylinder(Circle(Point(10, 10), 10), 10))) // 柱
再來看到 型別模式(Typed pattern),直接使用 重新定義 equals() 方法 中的一個例子作說明:
- class Point(val x: Int, val y: Int) {
- override def equals(a: Any) = a match {
- case that: Point => this.x == that.x && this.y == that.y
- case _ => false
- }
- override def hashCode = 41 * (41 + x) + y
- }
- class Point(val x: Int, val y: Int) {
- override def equals(a: Any) = {
- if(a.isInstanceOf[Point]) {
- val that = a.asInstanceOf[Point]
- this.x == that.x && this.y == that.y
- }
- false
- }
- override def hashCode = 41 * (41 + x) + y
- }
- def what(a: Any) = a match {
- case str : String => "字串"
- case list: List[_] => "串列"
- case set : Set[_] => "集合"
- case map : Map[_, _] => "字典"
- case _ => "別的東西"
- }
- println(what("text")) // 字串
- println(what(List(1, 2))) // 串列
- println(what(Set(1, 2, 3))) // 集合
- def what(a: Any) = a match {
- case list: List[String] => "字串串列"
- case _ => "別的東西"
- }
理由在於,Scala 的泛型(Generic)採用的是 型別抹除(Type erasure)的作法,加入群集後的物件基本上就失去型態資訊了(如果你熟悉 Java,這跟物件加入 Java 群集中意思是一樣的,所有的物件失去的型態資訊)。如果你執意運行以下的程式,結果將不正確:
- def what(a: Any) = a match {
- case list: List[String] => "字串串列"
- case _ => "別的東西"
- }
- val list1 = List("text")
- val list2 = List(1)
- println(what(list1)) // 字串串列
- println(what(list2)) // 字串串列
- def what(a: Any) = a match {
- case arr: Array[Int] => "整數陣列"
- case arr: Array[String] => "字串陣列"
- case _ => "別的東西"
- }
- val arr1 = Array(1)
- val arr2 = Array("text")
- println(what(arr1)) // 整數陣列
- println(what(arr2)) // 字串陣列
沒有留言:
張貼留言