版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/SunnyYoona/article/details/77301598
Scala有一個十分強大的模式比對機制.可以應用在很多場合:switch語句,類型檢查等等.此外Scala還提供了樣例類,對模式比對進行了優化.
1. 更好的switch http://gitlab.corp.qunar.com/jifeng.si/learningnotes/blob/master/IT/%E8%84%9A%E6%9C%AC/Scala/%5BScala%5DScala%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%8D%81%20%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D.md#1-switch
如下是Scala中C風格switch語句的等效代碼:
var sign = 2
val ch = '+'
ch match {
case '+' => sign = 1
case '-' => sign = -1
case _ => sign = 0
}
println(sign) // 1
與default等效的是捕獲所有模式(
case _模式
).有這樣一個捕獲所有的模式是有好處的.如果沒有模式能比對并且并沒有提供
case _
,代碼會抛出MatchError.
與C語言的switch不同的是,Scala模式比對不會像C語言那樣必須在每個分支的末尾顯示使用break語句防止進行下一分支.
與if類似,match也是表達式,而不是語句.前面代碼可以簡化為:
sign = ch match {
case '+' => 1
case '-' => -1
case _ => 0
}
可以在match表達式中使用任何類型,而不僅僅是數字:
color match{
case Color.RED => ...
case Color.BLACK => ...
...
}
2. 守衛 http://gitlab.corp.qunar.com/jifeng.si/learningnotes/blob/master/IT/%E8%84%9A%E6%9C%AC/Scala/%5BScala%5DScala%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%8D%81%20%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D.md#2
假如說我們想擴充上面第一個代碼以比對所有數字.在C風格switch語句中可以通過簡單的添加多個case标簽來實作:
switch(i){
case 0:
...
break;
case 1:
...
break;
...
case 9:
...
break;
default:
...
}
這如果放在Scala中會更簡單,給模式添加
守衛
(相當于一個判斷條件)即可:
ch match{
case '+' => sign = 1
case '-' => sign = -1
case _ if Character.isDigit(ch) => digit = Character.digit(ch, 10)
case _ => sign = 0
}
守衛可以是任何Boolean條件.
備注
模式總是從上往下進行比對的.如果守衛的這個模式不能比對,則捕獲所有的模式(`case _`)會被用來嘗試進行比對.
3. 模式中的變量 http://gitlab.corp.qunar.com/jifeng.si/learningnotes/blob/master/IT/%E8%84%9A%E6%9C%AC/Scala/%5BScala%5DScala%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%8D%81%20%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D.md#3
如果case關鍵字後面跟着一個變量名,那麼比對的表達式會被指派給那個變量,進而可以在後面中使用該變量:
val character = '1'
character match {
case '+' => println("this is +")
case '-' => println("this is -")
case ch => println("this is " + ch) // this is 1
}
上面代碼中如果給定字元不是'+'或者'-',給定字元則會指派給變量ch
也可以在守衛中使用變量:
val character = '5'
character match {
case '+' => println("this is +")
case '-' => println("this is -")
case ch if Character.isDigit(ch) => println("this is digit") // this is digit
}
4. 類型模式 http://gitlab.corp.qunar.com/jifeng.si/learningnotes/blob/master/IT/%E8%84%9A%E6%9C%AC/Scala/%5BScala%5DScala%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%8D%81%20%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D.md#4
可以根據表達式的類型進行比對:
val str:Any = "Hello World"
str match {
case s: String => println("this is string " + s) // this is string Hello World
case x: Int => println("this is integer " + x)
case ch => println("this is other " + ch)
}
在Scala中,我們更傾向于使用這種的模式比對,而不是使用
isInstanceOf
操作符.
注意模式中的變量名.當你在比對類型的時候,必須給出一個變量名(例如上例中的s,x).否則,将會拿對象本身來進行比對:
case _: BigInt => Int.MaxValue // 比對任何類型為BigInt的對象
case BigInt => -1 // 比對類型為Class的BigInt對象
5. 比對數組,清單和元組 http://gitlab.corp.qunar.com/jifeng.si/learningnotes/blob/master/IT/%E8%84%9A%E6%9C%AC/Scala/%5BScala%5DScala%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%8D%81%20%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D.md#5
比對數組中的内容,可以在模式中使用Array表達式:
def arrayMatch(arr:Array[String]) = arr match {
case Array("Hello") => println("the array only contain 'Hello'")
case Array(x,y) => println("the array contain two value " + x + " and " + y)
case Array(x,_*) => println("the array contain many values " + arr.mkString(","))
case _ => println("the other array")
}
arrayMatch(Array("Hello")) // the array only contain 'Hello'
arrayMatch(Array("Hello", "World")) // the array contain two value Hello and World
arrayMatch(Array("Hello", "World", "Yoona")) // the array contain many values Hello,World,Yoona
同樣也可以使用List表達式(或者使用
::
操作符)比對清單:
def listMatch(list:List[String]) = list match {
case "Hello" :: Nil => println("the list only contain 'Hello'")
case x :: y :: Nil => println("the list contain two value " + x + " and " + y)
case "Hello" :: tail => println("the list contain many values " + list)
case _ => println("the other list")
}
listMatch(List("Hello")) // the list only contain 'Hello'
listMatch(List("Hello", "World")) // the list contain two value Hello and World
listMatch(List("Hello", "World", "Yoona")) // the list contain many values List(Hello, World, Yoona)
同樣也可以使用元組表示法比對元組:
def pairMatch(t:Any) = t match {
case ("Hello", _) => println("the first value is 'Hello'")
case (x, "Hello") => println("the first value is " + x + " and the second value is 'Hello'")
case _ => println("the other tuple")
}
pairMatch(("Hello", "World")) // the first value is 'Hello'
pairMatch(("World", "Hello")) // the first value is World and the second value is 'Hello'
pairMatch(("Hello", "World", "Yoona")) // the other tuple
6. 提取器 http://gitlab.corp.qunar.com/jifeng.si/learningnotes/blob/master/IT/%E8%84%9A%E6%9C%AC/Scala/%5BScala%5DScala%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%8D%81%20%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D.md#6
前面我們看到模式是如何比對數組,清單和元組的,這些功能背後是
提取器機制,帶有從對象中提取值的unapply或unapplySeq方法的對象.unapply方法用于提取固定數量的對象,而unapplySeq提取的是一個序列.
arr match {
case Array(0, x) => ...
...
}
Array伴生對象就是一個提取器,定義了一個unapplySeq方法.該方法被調用時,以被執行比對動作的表達式作為參數.Array.unapplySeq(arr)産出一個序列的值,即數組中的值.第一個值與零進行比較,二第二個值被指派給x.
正規表達式是另一個适合使用提取器的場景,如果正規表達式中有分組,可以使用提取器來比對每個分組:
val pattern = "([0-9]+ ([a-z]+))".r
val str = "99 bottles"
str match {
// num = 99 item = bottles
case pattern(num, item) => ...
...
}
pattern.unapplySeq("99 bottles")生成一系列比對分組的字元串,這些字元串分别指派給變量num和item.
7. 變量聲明中的模式 http://gitlab.corp.qunar.com/jifeng.si/learningnotes/blob/master/IT/%E8%84%9A%E6%9C%AC/Scala/%5BScala%5DScala%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%8D%81%20%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D.md#7
之前我們可以看到模式彙總是可以帶變量的,同樣我們也可以在變量聲明中使用這樣的模式:
// x = 1 y = 2
val (x, y) = (1, 2)
這對于使用那些傳回對偶的函數是非常有用的:
val (q, r) = BigInt(10) /% 3
println("q value is " + q + " and r value is " + r)
// q value is 3 and r value is 1
/%方法傳回包含商和餘數的對偶,而這兩個值分别被變量q和r捕獲到.
8. for表達式中的模式 http://gitlab.corp.qunar.com/jifeng.si/learningnotes/blob/master/IT/%E8%84%9A%E6%9C%AC/Scala/%5BScala%5DScala%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%8D%81%20%E6%A8%A1%E5%BC%8F%E5%8C%B9%E9%85%8D.md#8-for
可以在for推導表達式中使用帶變量的模式.對于每一個周遊到的值,使用模式進行變量綁定:
import scala.collection.JavaConversions.propertiesAsScalaMap
for( (key, value) <- System.getProperties() ){
println(key + " = " + value)
}
在for表達式中,失敗的比對将被跳過(不會抛出異常),下面循環列印出所有值為空白的鍵,跳過所有其他鍵:
import scala.collection.JavaConversions.propertiesAsScalaMap
for( (key, "") <- System.getProperties() ){
println(key + " = ")
}
也可以使用守衛(注意if關鍵字出現在<-之後):
import scala.collection.JavaConversions.propertiesAsScalaMap
for( (key, value) <- System.getProperties() if value == ""){
println(key + " = " + value)
}