《快學scala》學習筆記--第14章 模式匹配和樣例類
本篇學習筆記是第二遍看《快學scala》所寫,對scala語言有一定的基礎
14.1 更好的switch
val ch: Char = '*'
ch match {
case '+' => println("加法")
case '*' => println("乘法")
case _ => println("木有")
}
match是一個表示式,而不是語句,會有返回值
val ch: Char = '*'
val x:Int = ch match {
case '+' => 0
case '*' => 1
case _ => 2
}
println(x)
14.2 守衛
守衛可以是任何布林條件,在下面的例子中,只有在守衛模式不能匹配的情況下才執行所有模式匹配
val ch:Char = 3
var sign:Int = 0
ch match {
case '+' => println("++++")
case '*' => println("====")
case _ if ch >= 0 && ch <= 9 => sign = ch
case _ => println("---")
}
println(sign)
14.3 模式中的變數
case _
是這種特性的特殊情況,變數名是_
val c: Char = '+'
val ch:Char = 9
var sign:Int = 0
ch match {
case '+' => println("++++")
case '*' => println("====")
case c if c >= 0 && c <= 9 => sign = c
case _ => println("---")
}
println(sign)
- 變數必須以小寫字母開頭
- 如果有一個小寫字母開頭的常量,則需要將它包在反引號中。
14.4 型別模式
匹配列表中的每一個元素的型別,將每一個元素對映成=>
後面的string
型別
val ls = List(1,3,4,"one","two",4.5)
val l = ls.map(_ match {
case x:Int => "int x:" + x
case s:String => "string s:" + s
case _ => "other"
})
l.foreach(println)
14.5 匹配組、列表和元組
要匹配陣列的內容,可以在模式中使用Array表示式,_*
符號匹配陣列中餘下的內容
val arr1 = Array(Array(0),Array(3,4),Array(0,4,6),Array("fad","fff"))
arr1.map(_ match {
case Array(0) => println(0)
case Array(x,y) => println(x,y)
case Array(0,_*) => println("...")
case _ => println("something")
})
匹配列表中的元素
val ls = List(List(0),List(3,4),List(0,4,5),List("fa","aaa","tttt"))
ls.map(_ match {
case 0::Nil => println(0)
case x :: y :: Nil => println(x,y)
case 0 :: tail => println(tail.foreach(println))
case x :: tail => println("====")
case _ => println("something")
})
匹配元組
val ls = List((0,4),("fa",0),(3,4))
ls.map(_ match {
case (0,_) => println(0)
case (x,y) => println(x,y)
case _ => println("something")
})
14.6 提取器
前一節中匹配陣列、列表和元素的功能是依靠提取器實現的
提取器機制:帶有從物件中提取值得unapply
和unapplySeq
方法得物件。unapply
提取固定數量得物件,而unapplySeq
提取得是一個序列
Array.unapplySeq(arr)
產出一個序列的值。
14.8 for表示式中的模式
val ls = List((1,2),(3,4),(4,5))
for ((k,v) <- ls if k % 2 == 1)
println(k,v)
14.9 樣例類
可以用模式匹配來匹配樣例類,並將屬性值繫結到變數
case class Dollar(value:Double)
case class Currency(value:Double,unit:String)
case object Yuan
val ls = List(Dollar(2.3),Currency(4.5,"10"),Yuan)
ls.map(_ match {
case Dollar(v) => println(s"Dollar:$v")
case Currency(_,u) => println(s"got $u")
case Yuan => println("lalala")
case _ => println("----")
})
*樣例類例項使用(),樣例物件不適用圓括號
==宣告樣例類的時候有如下幾件事自動發生:==
- 構造器中的每一個引數都成為val,除非被顯式地宣告為var
- 在伴生物件中提供apply方法,可以不用new關鍵字就能構造出相應的物件,比如Dollar(2.3)
- 提供unapply方法讓模式匹配可以工作
- 將生成toString
equals
copy
方法,除非顯式給出定義
14.10 copy方法和帶名引數
copy方法建立一個與現有物件值相同的新物件,可以用帶名引數修改某些屬性
val amt = Currency(34.2,"RUR")
val price = amt.copy()
val price1 = amt.copy(value = 13)
val price2 = amt.copy(unit = "CHF")
14.11 case語句中的中置表示法
如果unapply方法產出一個對偶,可以在case語句中使用中置表示法,19章將會看到將解析結果組合在一起的`~
樣例類:
result match {case p ~ q => ...} //等同於case ~(p,q)
*如果操作符以冒號結尾,則他是從右向左結合的
中置表示法可以用於任何返回對偶的unapply方法:
case object +: {
def unapply[T](input: List[T]):Option[(T,List[T])] =
if(input.isEmpty) None else Some((input.head,input.tail))
}
1+:7+:2+:Nil match {
case first +: second +: rest => println(first + second + rest.length)
}
14.12 匹配巢狀結構
三個樣例類繼承Item抽象類,Bundle和Boolean類巢狀Article和Bundle樣例類,
可以用@
表示法將巢狀的值繫結到變數
abstract class Item
case class Article(desc:String,price:Double) extends Item//繼承同一個抽象類
case class Bundle(desc:String,discount:Double,item:Item) extends Item
val peak = Bundle("Father's Day is special",20.0,Article("scala is nice",45.5))
case class Boolean(desc:String,dis:Double,item:Item*)//Item抽象類列表
val amt = Boolean("great",23.3,Bundle("nice",45.4,Article("wonderful",45.4)),Article("scala is nice",45.5))
val ls = List(peak,amt)
ls.map(_ match {
case Bundle(_,_,Article(desc,price)) => println(s"bundle ${desc}:${price}")
case Boolean(_,_,art @ Bundle(_,_,_),rest @ _*) => println(s"boolean ${art.desc}:${art.discount},${rest.head}")
case Boolean(_,_,art @ Bundle(_,_,_),rest) => println(s"boolean ${art.desc}")//匹配繼承Item抽象類的元素只有兩個
case _ => println("None")
})
14.14 密封類
當用樣例類來做模式匹配的時候,想讓編譯器幫助確保已經列出了所有可能的選擇,要達到這個目的,需要將樣例類的通用超類宣告為sealed
- 密封類的所有子類都必須在與該密封類相同的檔案中定義。
- 如果某個類是密封的,那麼在編譯期所有子類就是可知的,因此==編譯器可以檢查模式語句的完整性==,讓同一組樣例類都擴充套件自某個密封的類或者特質是一個好的做法
sealed abstract class Amount
case class Red(bottom:Double) extends Amount
case object Green extends Amount
case object Gray extends Amount
val color:Amount = Red(3.4)//需要顯式宣告color的型別,不然就會變成Red
color match {
case Red(_) => println("red")
case Green =>println("green")
case Gray => println("fad")
}
14.16 Option型別
標準類庫的Option型別用樣例類來表示那種可能存在,也可能不存在的值。這比空字串更加清晰,比null做法更加安全。
* map類的get方法返回一個Option。如果對於給定的鍵沒有對應的值,則get返回None,如果有值,該值包在Some中返回
* 請以不要直接使用get方法,返回None的情況可能會使程式死掉,儘量用getOrElse
Option[+A]密封抽象類,Some和None繼承Option類
14.17 偏函式
被包括在花括號內的一組case語句是一個偏函式——==並非對所有輸入值都有定義的函式==。它是PartialFunction[A,B]類的一個例項,該類有兩個方法:apply方法從匹配到的模式計算函式值,而isDefinedAt
方法在輸入至少匹配其中一個模式時返回true
val f:PartialFunction[Char,Int] = {
case '+' => 1
case '-' => -1
}
println(f('+'))//1
//println(f('0'))//error
println(f.isDefinedAt('0'))//false