前言
Scala中的模式匹配類似于Java中的switch語法,但是更加強大蒸辆。
模式匹配語法中征炼,采用match關(guān)鍵字聲明,每個分支采用case關(guān)鍵字進行聲明躬贡,當(dāng)需要匹配時谆奥,會從第一個case分支開始,如果匹配成功拂玻,那么執(zhí)行對應(yīng)的邏輯代碼酸些,如果匹配不成功,繼續(xù)執(zhí)行下一個分支進行判斷檐蚜。如果所有case都不匹配魄懂,那么會執(zhí)行case _分支,類似于Java中default語句闯第。
語法:
val 返回值 變量 match{
????case <條件> => {匹配上的表達式}
????case <條件> => {匹配上的表達式}
????...
????case _ => {類似于Java中default語句}
}
案例演示
案例一演示
模擬 菜單選項 輸入校驗
def main(args: Array[String]): Unit = {
val context =
"""
|歡迎來到 xxx圖書館管理系統(tǒng)
|1. 借書
|2. 還書
|3. 查看庫存
|""".stripMargin
println(context)
print("請輸入操作選項:")
val str: String = StdIn.readLine()
str match {
case "1" => println("給你一本書")
case "2" => println("還書已完成")
case "3" => println("還有19000本書")
case _ => println("暫時不支持該功能")
}
}
借書
歡迎來到 xxx圖書館管理系統(tǒng)
1. 借書
2. 還書
3. 查看庫存
請輸入操作選項:1
給你一本書
其他操作
歡迎來到 xxx圖書館管理系統(tǒng)
1. 借書
2. 還書
3. 查看庫存
請輸入操作選項:4
暫時不支持該功能
注意:若不指定 case _
市栗,程序若是匹配不上,那么將拋出異常
str match {
case "1" => println("給你一本書")
case "2" => println("還書已完成")
case "3" => println("還有19000本書")
}
歡迎來到 xxx圖書館管理系統(tǒng)
1. 借書
2. 還書
3. 查看庫存
請輸入操作選項:4
Exception in thread "main" scala.MatchError: 4 (of class java.lang.String)
at com.admin.xxx.collection.Match$.main(Match.scala:22)
at com.admin.xxx.collection.Match.main(Match.scala)
模式匹配一旦匹配到條件之后,執(zhí)行完條件后面的塊表達式之后會自動退出
模式匹配一般在最后會加上一個case x/case _ 用于匹配其他情況
案例二演示
上超市購物咳短,通過商品名稱匹配填帽,返回對應(yīng)商品的單價
def main(args: Array[String]): Unit = {
print("請輸入商品名稱:")
val str: String = StdIn.readLine()
val price= str match {
case "可樂" => 3.0
case "雞翅" => 9.8
case "衣服" => 56.7
case _ => println("暫無此商品")
}
println(s"商品單價 $price")
}
測試1
請輸入商品名稱:可樂
商品單價 3.0
測試2
請輸入商品名稱:薯片
暫無此商品
商品單價 ()
模式匹配有返回值,返回值就是符合條件的分支的塊表達式的結(jié)果值
通過上面兩個案例說明了 模式匹配
的基本用法,接下來看看模式匹配的高階應(yīng)用咙好。
模式守衛(wèi)
類似與 for 中的守衛(wèi)篡腌,可以用于做一些條件過濾。
語法:
模式匹配守衛(wèi):
變量名 match {
????case 條件 if (布爾表達式) => ...
????case 條件 if (布爾表達式) => ...
????case 條件 if (布爾表達式) => ...
????...
}
案例:校驗用戶密碼敷扫;
- 首先要滿足 8位及以上長度
- 不能為純數(shù)字
- 不能為純字母
def main(args: Array[String]): Unit = {
print("請輸入你的密碼:")
val str: String = StdIn.readLine()
str match {
case _ if str.length <8 => println("密碼長度不夠")
case _ if "[0-9]*".r.pattern.matcher(str).matches => println("不能全為數(shù)字 ")
case _ if "[a-zA-Z]*".r.pattern.matcher(str).matches => println("不能全為字母")
case pass => println(s"密碼符合:$pass")
}
}
長度校驗
請輸入你的密碼:13ffd11
密碼長度不夠
數(shù)字校驗
請輸入你的密碼:2222414134132
不能全為數(shù)字
字母校驗
請輸入你的密碼:afafasfasdfas
不能全為字母
混合輸入
請輸入你的密碼:123abc123
密碼符合:123abc123
使用模式匹配時,必須指定
條件
诚卸。葵第;如:
case pass => println(s"密碼符合:$pass")
當(dāng)
=>
未用到條件
時,可以將 參數(shù)定義成_
合溺;如:
case _ if str.length <8 => println("密碼長度不夠")
類型匹配
匹配常量
scala中卒密,模式匹配可以匹配所有的字面量,包括字符串棠赛,字符哮奇,數(shù)字膛腐,布爾值等等。
def main(args: Array[String]): Unit = {
def matchConstant(x:Any): Unit ={
x match {
case 1 => println("數(shù)字1")
case 0.0 => println("浮點數(shù)0.0")
case "hello" => println("操作符hello")
case '+' => println("Char +")
case true => println("布爾類型 true")
}
}
}
數(shù)字匹配
matchConstant(1)
數(shù)字1
浮點數(shù)匹配
matchConstant(0.0)
浮點數(shù)0.0
字符串匹配
matchConstant("hello")
操作符hello
字符匹配
matchConstant('+')
Char +
布爾匹配
matchConstant(true)
布爾類型 true
匹配外部變量
def main(args: Array[String]): Unit = {
def matchConstant(x:Any): Unit ={
val Name=1
x match {
case Name=> println("數(shù)字1")
}
}
}
匹配數(shù)字1
matchConstant(1)
數(shù)字1
更改代碼鼎俘,加個 x
def main(args: Array[String]): Unit = {
def matchConstant(x:Any): Unit ={
val Name=1
x match {
case Name => println(s"Name=${Name}")
case x=> println(s"x=${x}")
}
}
}
測試哲身,輸入2 匹配的是 x ,??贸伐,沒問題勘天。
matchConstant(2)
x=2
再更改代碼;將case Name 改成小寫 name
def matchConstant(x:Any): Unit ={
val Name=1.1
x match {
case name => println(s"Name=$name")
case x=> println(s"x=${x}")
}
}
測試
matchConstant(2)
Name=2
如果模式匹配中需要使用外部變量作為匹配的條件,此時需要變量名首字母大寫
匹配類型
需要進行類型判斷時捉邢,可以使用isInstanceOf[T]和asInstanceOf[T]脯丝,也可使用模式匹配實現(xiàn)同樣的功能。
語法:
變量名 match {
????case x: 類型 => ...
????case _: 類型 => ...
????...
}
def main(args: Array[String]): Unit = {
def matchConstant(x:Any): Unit ={
x match {
case name:Int => println(s"這是Int類型")
case name:String=> println(s"這是String類型")
case name:Double=> println(s"這是Double類型")
case name:Boolean=> println(s"這是Boolean類型")
}
}
}
匹配String類型
matchConstant("hello")
這是String類型
匹配Int類型
matchConstant(123)
這是Int類型
匹配Boolean類型
matchConstant(false)
這是Boolean類型
匹配數(shù)組
scala模式匹配可以對集合進行精確的匹配伏伐,例如匹配只有兩個元素的宠进、且第一個元素為0的數(shù)組。
案例:
def main(args: Array[String]): Unit = {
val arr=Array[Any]("hello",123)
arr match {
case Array(x,y) => println(s"參數(shù)只能包含兩個; 數(shù)據(jù)值:$x,$y")
case _ if arr.length>5 => println("數(shù)組長度必須大于5個元素")
case Array(x:Int,y:String,z:Double) => println("參數(shù)只能包含兩個; 并且 x為Int類型藐翎,y為String類型材蹬,z為Double類型")
case Array(x,y,_*) => println(s"參數(shù)至少包含兩個; 數(shù)據(jù)值:$x,$y")
case _ => println("數(shù)組其中格式匹配")
}
}
長度必須大于五個
val arr=Array[Any]("hello",123,2,3.4,3,'a')
數(shù)組長度必須大于5個元素
參數(shù)類型匹配
val arr=Array[Any](1,"hello",3.4)
參數(shù)只能包含兩個; 并且 x為Int類型,y為String類型阱高,z為Double類型
其他的就不試了
匹配List
第一種方式:和數(shù)組的一樣
def main(args: Array[String]): Unit = {
val list = List[Any](1,5,8,2,10)
//第一種匹配方式
list match {
case List(x) => println("list中只有一個元素")
case List(x:Int,y:String) => println("list中有兩個元素")
case List(x,_*) => println("list中至少有一個元素")
}
//第二種匹配方式
list match {
case x :: Nil => println("list中只有一個元素")
case x :: y :: Nil => println("list中有兩個元素")
case (x:String) :: y :: tail => println(s"String list中至少有一個元素: ${x} ${tail}" )
case (x:Int) :: y :: tail => println(s"Int list中至少有一個元素: ${x} ${tail}" )
}
}
第二種方式:剛方式
def main(args: Array[String]): Unit = {
val list = List[Any](1,5,8,2,10)
//第二種匹配方式
list match {
case x :: Nil => println("list中只有一個元素")
case x :: y :: Nil => println("list中有兩個元素")
case (x:String) :: y :: tail => println(s"String list中至少有一個元素: ${x} ${tail}" )
case (x:Int) :: y :: tail => println(s"Int list中至少有一個元素: ${x} ${tail}" )
}
}
tail :表示剩下的元素赚导;除去 x 和 y 對應(yīng)的元素外,剩下的都是tail
def main(args: Array[String]): Unit = {
val list = List[Any](1,5,8,2,10)
//第二種匹配方式
list match {
case (x:Int) :: y :: tail => println(s"Int list中至少有一個元素: ${x} ${y} ${tail}" )
}
}
Int list中至少有一個元素: 1 5 List(8, 2, 10)
這里的 tail 只是取個名而已赤惊,實際上叫啥都可以
def main(args: Array[String]): Unit = {
val list = List[Any](1,5,8,2,10)
//第二種匹配方式
list match {
case (x:Int) :: y :: aa=> println(s"Int list中至少有一個元素: ${x} ${y} ${aa}" )
}
}
Int list中至少有一個元素: 1 5 List(8, 2, 10)
注意:類型匹配需要帶()
吼旧;如
case (x:String) :: y :: tail => println(s"String list中至少有一個元素: ${x} ${tail}" )
匹配元組
def main(args: Array[String]): Unit = {
val tuple =("zhangsan",18,"北京朝陽區(qū)")
tuple match {
case (x,y,z) => println(s"${x},${y},${z}")
}
}
zhangsan,18,北京朝陽區(qū)
匹配元組的時候,變量是幾元元組匹配條件中就必須是幾元元組
匹配元組的應(yīng)用場景
有一批數(shù)據(jù),元組套元組未舟,不知道是多少層圈暗。
val tlist: List[(String, (String, (String, (String, Int))))] = List(
("寶安區(qū)1",("寶安中學(xué)1",("王者班1",("王昭君1",201)))),
("寶安區(qū)2",("寶安中學(xué)2",("王者班2",("王昭君2",202)))),
("寶安區(qū)3",("寶安中學(xué)3",("王者班3",("王昭君3",203)))),
("寶安區(qū)4",("寶安中學(xué)4",("王者班4",("王昭君4",204))))
)
現(xiàn)在以后需要,獲取各個班中的學(xué)生名稱(王昭君N
)
普通的方式裕膀。
def main(args: Array[String]): Unit = {
val tlist: List[(String, (String, (String, (String, Int))))] = List(
("寶安區(qū)1",("寶安中學(xué)1",("王者班1",("王昭君1",201)))),
("寶安區(qū)2",("寶安中學(xué)2",("王者班2",("王昭君2",202)))),
("寶安區(qū)3",("寶安中學(xué)3",("王者班3",("王昭君3",203)))),
("寶安區(qū)4",("寶安中學(xué)4",("王者班4",("王昭君4",204))))
)
for(e <- tlist){
val value: String = e._2._2._2._1
println(value)
}
}
輸出結(jié)果
王昭君1
王昭君2
王昭君3
王昭君4
為了獲取里面的數(shù)據(jù)员串,需要寫成這樣的形式e._2._2._2._1
;開發(fā)時也許還知道各個._2 是什么昼扛,但是過一段時間寸齐,可能就忘了,此種方式出現(xiàn)的問題就是可讀性極差抄谐。
def readStuName(tlist:List[(String, (String, (String, (String, Int))))] ): Unit ={
for(e <- tlist){
val value: String = e._2._2._2._1
println(value)
}
}
若該list 是別人傳給我們的渺鹦,不看原數(shù)據(jù),更不明白是什么了蛹含。
模式匹配的方式
def main(args: Array[String]): Unit = {
def readStuName(tlist:List[(String, (String, (String, (String, Int))))] ): Unit ={
for(e <- tlist){
e match {
case (area,(school,(clazz,(stuName,id)))) => println(stuName)
}
}
}
val tlist: List[(String, (String, (String, (String, Int))))] = List(
("寶安區(qū)1",("寶安中學(xué)1",("王者班1",("王昭君1",201)))),
("寶安區(qū)2",("寶安中學(xué)2",("王者班2",("王昭君2",202)))),
("寶安區(qū)3",("寶安中學(xué)3",("王者班3",("王昭君3",203)))),
("寶安區(qū)4",("寶安中學(xué)4",("王者班4",("王昭君4",204))))
)
readStuName(tlist)
}
結(jié)果
王昭君1
王昭君2
王昭君3
王昭君4
同樣獲取結(jié)果毅厚,采用模式匹配的方式,可讀性大大提高
def readStuName(tlist:List[(String, (String, (String, (String, Int))))] ): Unit ={
for(e <- tlist){
e match {
case (area,(school,(clazz,(stuName,id)))) => println(stuName)
}
}
}
即使沒有源數(shù)據(jù)浦箱,也能明白各個字段的意思吸耿;
當(dāng)然獲取數(shù)據(jù)的方式也更加方便祠锣;比如獲取學(xué)生的姓名及所在的學(xué)校
def readStuName(tlist:List[(String, (String, (String, (String, Int))))] ): Unit ={
for(e <- tlist){
e match {
case (area,(school,(clazz,(stuName,id)))) => println(s"姓名:${stuName} 學(xué)校:${school}")
}
}
}
結(jié)果
姓名:王昭君1 學(xué)校:寶安中學(xué)1
姓名:王昭君2 學(xué)校:寶安中學(xué)2
姓名:王昭君3 學(xué)校:寶安中學(xué)3
姓名:王昭君4 學(xué)校:寶安中學(xué)4
如果解決上面的太復(fù)雜了,還可以進行簡寫
def readStuName(tlist:List[(String, (String, (String, (String, Int))))] ): Unit ={
tlist.foreach({case (area,(school,(clazz,(stuName,id)))) => println(s"姓名:${stuName} 學(xué)校:${school}")})
}
然后在進行簡化 不要()
咽安;直接改成這樣伴网。
tlist.foreach{case (area,(school,(clazz,(stuName,id)))) => println(s"姓名:${stuName} 學(xué)校:${school}")}
匹配對象和樣例類
樣例類: 其實就是伴生類和伴生對象的封裝
語法:
case class 類名([val/var]屬性名:類型,...)
定義一個類:
class Person(val name:String,val age:Int,val sex:Char)
這是一個普通類,若要定義成樣例類板乙,需要加上 case
case class Person(val name:String,val age:Int,val sex:Char)
獲取樣例類的對象是偷;通過 apply
Person.apply("張三",18,'男')
apply
可以進行省略;所以可以寫成下面這種方式募逞。
Person("張三",18,'男')
獲取樣例對象: Person.apply(值,...) / Person(值,...)
獲取樣例類數(shù)據(jù)
println(person.name) // 張三
println(person.age) // 18
println(person.sex) // 男
樣例類中屬性不用val/var修飾的時候蛋铆,默認就是val修飾;使用 val修飾的屬性不能進行修改放接。
使用樣例類進行模式匹配
def main(args: Array[String]): Unit = {
val person=Person("張三",18,'男')
person match {
case Person(x,y,z) => println(s"姓名:$x刺啦;年齡:$y;性別:$z")
}
}
姓名:張三纠脾;年齡:18玛瘸;性別:男
普通類可以進行模式匹配嗎?
我們試一試苟蹈;定義一個Student類
class Student(val name:String,val age:Int,val sex:Char)
創(chuàng)建對象
val student=new Student("李四",20,'男')
進行模式匹配 糊渊;提醒我們報錯了
Cannot resolve method student.unapply
Cannot resolve symbol student
普通類不能直接用于模式匹配,如果想要讓普通類用于模式匹配必須在伴生對象中定義unapply方法
定義一個 Student
伴生對象;實現(xiàn) unapply
object Student{
def unapply(arg: Student): Option[(String, Int, Char)] = {
if(arg == null) None
else Some((arg.name,arg.age,arg.sex))
}
}
此時 普通類就可以實現(xiàn)模式匹配了
student match {
case Student(x,y,z) => println(s"姓名:$x慧脱;年齡:$y渺绒;性別:$z")
}
姓名:李四;年齡:20菱鸥;性別:男
變量聲明宗兼,for循環(huán)模式匹配
定義一個元組
val t =("張三",18)
若要取值的話需要使用 ._n
的方式。
val t =("張三",18)
println(t._1)
println(t._2)
其實可以換種方式
val (name,age) =("張三",18)
println(name)
println(age)
當(dāng)然也可以用再List
上
val List(x,y,z)=List(1,2,3)
println(x,y,z)
對象也是可以的氮采。
val Person(name,age,sex)=Person("張三",18,'男')
println(name,age,sex)
除了這些殷绍,數(shù)組,set, map 等可以用變量聲明
的方式簡化模式匹配
鹊漠,此種方式類似于(如下)方式主到。
val person=Person("張三",18,'男')
person match {
case Person(name,age,sex) => println(s"$name,$age,$sex")
}
雖然用的是Person
做案例,其他類型都是一樣躯概。
說完變量聲明
登钥;再說說for循環(huán)模式
定義一個map
val map=Map("name"-> "張三","age"->18,"sex"->'男')
想這種很方便的鍵值對
方式,使用模式匹配拿數(shù)據(jù)就很簡單了楞陷。
for((k,v)<-map){
println(s"$k = $v")
}
name = 張三
age = 18
sex = 男
對象亦是如此
def main(args: Array[String]): Unit = {
val person1=Person("張三",18,'男')
val person2=Person("張三",18,'男')
val person3=Person("張三",18,'男')
val person4=Person("張三",18,'男')
val personList=List(person1,person2,person3,person4)
for(Person(name,age,sex)<-personList){
println(s"$name, $age,$sex")
}
}
雖然內(nèi)容一樣怔鳖,只是我比較懶茉唉,難道改了(copy 很香)固蛾,但都屬于不同的對象结执。
張三, 18,男
張三, 18,男
張三, 18,男
張三, 18,男
偏函數(shù)中的模式匹配
什么叫偏函數(shù)
偏函數(shù)也是函數(shù)的一種,通過偏函數(shù)我們可以方便的對輸入?yún)?shù)做更精確的檢查艾凯。例如該偏函數(shù)的輸入類型為List[Int]献幔,而我們需要的是第一個元素是0的集合,這就是通過模式匹配實現(xiàn)的趾诗。
偏函數(shù)定義
val second: PartialFunction[List[Int], Option[Int]] = {
case x :: y :: _ => Some(y)
}
注:該偏函數(shù)的功能是返回輸入的List集合的第二個元素
偏函數(shù): 沒有match關(guān)鍵字的模式匹配稱之為偏函數(shù)
案例:從元組集合中獲取 學(xué)生姓名及學(xué)校(和上面的案例一樣)
val list: List[(String, (String, (String, (String, Int))))] = List(
("寶安區(qū)1",("寶安中學(xué)1",("王者班1",("王昭君1",201)))),
("寶安區(qū)2",("寶安中學(xué)2",("王者班2",("王昭君2",202)))),
("寶安區(qū)3",("寶安中學(xué)3",("王者班3",("王昭君3",203)))),
("寶安區(qū)4",("寶安中學(xué)4",("王者班4",("王昭君4",204))))
)
使用模式匹配的方式獲取
list.foreach({
case (area,(school,(clazz,(stuName,id)))) => println(s"姓名:$stuName,學(xué)校:$school")
})
因為只有一句表達式蜡感;()
可以進行省略。
list.foreach{case (area,(school,(clazz,(stuName,id)))) => println(s"姓名:$stuName,學(xué)校:$school")}
姓名:王昭君1,學(xué)校:寶安中學(xué)1
姓名:王昭君2,學(xué)校:寶安中學(xué)2
姓名:王昭君3,學(xué)校:寶安中學(xué)3
姓名:王昭君4,學(xué)校:寶安中學(xué)4
使用偏函數(shù)的方式
val fun1:PartialFunction[(String,(String,(String,(String,Int)))),Unit]={
case (area,(school,(clazz,(stuName,id)))) => println(s"姓名:$stuName,學(xué)校:$school")
}
// 調(diào)用
list.foreach(fun1)
fun1:函數(shù)名稱
(String,(String,(String,(String,Int)))) : 這個只是傳入的參數(shù)類型恃泪;就是集合單個元組中的類型郑兴。
Unit :表示返回值類型,因為這里只是打印所有不需返回
姓名:王昭君1,學(xué)校:寶安中學(xué)1
姓名:王昭君2,學(xué)校:寶安中學(xué)2
姓名:王昭君3,學(xué)校:寶安中學(xué)3
姓名:王昭君4,學(xué)校:寶安中學(xué)4
帶返回的偏函數(shù)贝乎;不在偏函數(shù)中進行打印
val fun1:PartialFunction[(String,(String,(String,(String,Int)))),String]={
case (area,(school,(clazz,(stuName,id)))) => s"姓名:$stuName,學(xué)校:$school"
}
// 調(diào)用并打印
list.foreach(e=> println(fun1(e)))
這里不能簡寫這樣如下情连;因為需要打印,無法嵌套傳參览效,必須要明確指定傳參却舀。
list.foreach(println(fun1))
目前案例比較簡單,可能從視覺上來說锤灿,第一種的模式匹配的方式挽拔,看起來比較簡潔。偏函數(shù)需要定義一個函數(shù)(包裹模式匹配定義)但校;所以覺得特麻煩螃诅。若業(yè)務(wù)復(fù)雜起來,往往偏函數(shù)的方式更加合理始腾。具體的原因:函數(shù)就是比較好州刽,真正調(diào)用時,這樣的代碼(如下
)還不好嗎?
// 調(diào)用
list.foreach(fun1)
除了foreach
在很多地方都可以用到偏函數(shù)浪箭;如map
val newList: List[String] = list.map(e => fun1(e))
// 打印
println(newList.mkString(","))
姓名:王昭君1,學(xué)校:寶安中學(xué)1,姓名:王昭君2,學(xué)校:寶安中學(xué)2,姓名:王昭君3,學(xué)校:寶安中學(xué)3,姓名:王昭君4,學(xué)校:寶安中學(xué)4
最后:
關(guān)于模式匹配的知識到這里也就完了穗椅,有什么疑問或者我沒有補充到的,歡迎下方探討奶栖。