Scala之模式匹配
目录
一、概述
Scala中的模式匹配类似于Java中的switch语法,但是更加强大。
模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _分支,类似于Java中default语句。
二、基本语法
package com.scala.charpter08
import scala.io.StdIn
object $01_MathDefine {
/**
* 模式匹配语法:
* 变量 match{
* case 条件1 => {
* ....
* }
* case 条件1 => {
* ....
* }
* .....
* }
*
* 模式匹配有返回值,返回值为符合条件的分支的{}中最后一个表达式的结果值
* @param args
*/
def main(args: Array[String]): Unit = {
val word = StdIn.readLine("请输入一个单词")
val result = word match {
case "hadoop" =>
println("单词是hadoop")
println("...........")
10
case "spark" =>
println("单词是spark")
println("...........")
20
case x=>
println(s"单词是${x}")
}
println(result)
}
}
说明
(1)如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句,若没有case _ 分支,那么会抛出MatchError。
(2)每个case中,不用break语句,自动中断case。
(3)match case语句可以匹配任何类型,而不只是字面量。
(4)=> 后面的代码块,是作为一个整体执行,可以使用{}括起来,也可以不括。
三、模式守卫
package com.scala.charpter08
import scala.io.StdIn
object $02_MathIf {
/**
* 模式匹配守卫:
* 变量 match {
* case 条件1 if (布尔表达式) =>{
* .....
* }
* case 条件1 if (布尔表达式) =>{
* .....
* }
* .....
* }
* @param args
*/
def main(args: Array[String]): Unit = {
val line = StdIn.readLine("请输入一个单词:")
line match {
case x if(x.contains("hadoop"))=>
println("单词是hadoop")
case x if(x.contains("spark"))=>
println("单词是spark")
case _=>
println("其他。。。")
}
}
}
说明
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。
四、模式匹配类型
4.1 匹配常量
object TestMatchVal {
def main(args: Array[String]): Unit = {
println(describe(6))
}
def describe(x: Any) = x match {
case 5 => "Int five"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
}
}
说明
scala中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。
4.2 匹配类型
package com.scala.charpter08
import scala.util.Random
object $03_MatchType {
def main(args: Array[String]): Unit = {
val list: List[Any] = List(2,9.0,false,"hello")
val index: Int = Random.nextInt(list.length)
val parm = list(index)
println(parm)
parm match {
case _:String=>
println("string.....")
case _:Int=>
println("Int......")
case _:Double=>
println("double....")
case _=>
println("其他。。。")
}
}
}
说明
需要进行类型判断时,可以使用前文所学的isInstanceOf[T]和asInstanceOf[T],也可使用模式匹配实现同样的功能。
4.3 匹配数组
package com.scala.charpter08
object $04_MatchArray {
def main(args: Array[String]): Unit = {
val arr:Array[Any] = Array(78)
arr match {
//匹配数组只有一个元素的情况
case Array(x)=>
println(s"数组只有一个元素${x}")
//匹配数组有四个元素的情况,类型为。。。
case Array(x:Int,y:Double,a,z:String)=>
println(s"数组有四个元素,类型分别为。。。。。。${x}")
//匹配数组有四个元素
case Array(x,y,a,z)=>
println(s"数组有四个元素,类型分别为。。。。。。${x}")
//匹配数组至少有一个元素
case Array(x,_*)=>
println("数组至少有一个元素。。。")
//其他情况
case _=>
println("其他。。。")
}
}
}
说明
scala模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为0的数组。
4.4 匹配列表
package com.scala.charpter08
object $05_MatchList {
def main(args: Array[String]): Unit = {
val list: List[Any] = List(1,8.0,false,"scala",11)
//与Array类似进行匹配
list match {
//匹配列表只有一个元素的情况
case List(x)=>
println(s"数组只有一个元素${x}")
//匹配列表有四个元素的情况,类型为。。。
case List(x:Int,y:Double,a,z:String)=>
println(s"数组有四个元素,类型分别为。。。。。。${x}")
//匹配列表有四个元素
case List(x,y,a,z)=>
println(s"数组有四个元素,类型分别为。。。。。。${x}")
//匹配列表至少有一个元素
case List(x,_*)=>
println("数组至少有一个元素。。。")
//其他情况
case _=>
println("其他。。。")
}
list match {
case x::Nil=>
println("列表只有一个元素")
case (x:String)::y::(z:Int)::a::Nil=>
println("列表有四个元素,类型分别为。。。。")
case x::y::z::a::Nil=>
println("列表有四个元素")
case x::tail=>
println(s"列表至少有一个元素${x} ${tail}")
case _=>
println("其他")
}
val list2:List[Any] = List(1,2,3,4)
//泛型擦除,集合的泛型主要用来在代码编写期间规定集合中数据元素的类型
//泛型在编译的时候会被擦掉,任何list都能匹配上
list match {
case x:List[Boolean]=>
println("boolean....")
case x:List[String]=>
println("string.....")
case x:List[Int]=>
println("int........")
case x:List[Any]=>
println("any....")
case _=>
println("........")
}
}
}
4.5 匹配元组
package com.scala.charpter08
object $06_MatchTope {
def main(args: Array[String]): Unit = {
val t1:(Any,Any,Any)=("zhangsna","20","shezhen")
t1 match {
case (x:String,y:Int,z:String)=>
println("...........")
case (_,_,_)=>
println("元素个数是3个")
case _=>
println("其他")
}
val list = List(
("宝安",("宝安中学",("大神班",("zhangsan",20)))) ,
("宝安",("宝安中学",("大神班",("lisi",20)))),
("宝安",("宝安中学",("大神班",("wangwu",20)))),
("宝安",("宝安中学",("大神班",("zhaoliu",20))))
)
list.map(x=>{
x match {
case (region,(school, (className,(stuName,age))))=>
stuName
}
}).foreach(println)
}
}
4.6 匹配对象及样例类
package com.scala.charpter08
object $07_MatchClass {
case class Person(val name:String,var age:Int,addr: String)
class Student(val name:String,var age:Int,val addr:String)
object Student{
def apply(name: String, age: Int, addr: String): Student = new Student(name, age, addr)
def unapply(stu: Student): Option[(String, Int, String)] = {
if(stu==null)
None
else
Some((stu.name,stu.age,stu.addr))
}
}
abstract class Sex
case object Man extends Sex
case object Woman extends Sex
def xx(sex:Sex)={
println(sex)
}
/**
* 样例类:
* 语法:case class 类名([var/val] 属性名:类型,....)
* 创建样例类对象:类名(属性值,....)
* 属性不用var/val修饰时默认就是val
* 样例类其实就是伴生对象和伴生类的简写
*
* 样例对象:
* 语法:case object object名称
* 一般用于枚举
*
*
* 普通类不能直接用于模式匹配,要想用于模式匹配需要在伴生对象中添加unapply方法
*
* @param args
*/
def main(args: Array[String]): Unit = {
val person = Person("zhangsan",20,"shenzhen")
println(person.name)
println(person.addr)
println(person.age)
person.age=90
println(person.age)
// person.addr = "kkk"
val lisi: Student = Student("lisi",9,"shanghai")
lisi.age=990
println(lisi.name)
println(lisi.age)
xx(Man)
person match {
case Person(x,y,z)=>
println(x,y,z)
}
val sudent = new Student("wangwu",44,"ilovyou")
sudent match {
case Student(x,y,z)=>
println(x,y,z)
}
}
}
五、变量声明中的模式匹配
case class Person(name: String, age: Int)
object TestMatchVariable {
def main(args: Array[String]): Unit = {
val (x, y) = (1, 2)
println(s"x=$x,y=$y")
val Array(first, second, _*) = Array(1, 7, 2, 9)
println(s"first=$first,second=$second")
val Person(name, age) = Person1("zhangsan", 16)
println(s"name=$name,age=$age")
}
}
六、for表达式中的模式匹配
object TestMatchFor {
def main(args: Array[String]): Unit = {
val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
for ((k, v) <- map) { //直接将map中的k-v遍历出来
println(k + " -> " + v) //3个
}
println("----------------------")
//遍历value=0的 k-v ,如果v不是0,过滤
for ((k, 0) <- map) {
println(k + " --> " + 0) // B->0
}
println("----------------------")
//if v == 0 是一个过滤的条件
for ((k, v) <- map if v >= 1) {
println(k + " ---> " + v) // A->1 和 c->33
}
}
}
七、偏函数中的模式匹配
偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为List[Int],而我们需要的是第一个元素是0的集合,这就是通过模式匹配实现的。
1)偏函数定义
val second: PartialFunction[List[Int], Option[Int]] = {
case x :: y :: _ => Some(y)
}
注:该偏函数的功能是返回输入的List集合的第二个元素
2)偏函数原理
上述代码会被scala编译器翻译成以下代码,与普通函数相比,只是多了一个用于参数检查的函数——isDefinedAt,其返回值类型为Boolean。
val second = new PartialFunction[List[Int], Option[Int]] {
//检查输入参数是否合格
override def isDefinedAt(list: List[Int]): Boolean = list match {
case x :: y :: _ => true
case _ => false
}
//执行函数逻辑
override def apply(list: List[Int]): Option[Int] = list match {
case x :: y :: _ => Some(y)
}
}
3)偏函数使用
偏函数不能像second(List(1,2,3))这样直接使用,因为这样会直接调用apply方法,而应该调用applyOrElse方法,如下
second.applyOrElse(List(1,2,3), (_: List[Int]) => None)
applyOrElse方法的逻辑为 if (ifDefinedAt(list)) apply(list) else default。如果输入参数满足条件,即isDefinedAt返回true,则执行apply方法,否则执行defalut方法,default方法为参数不满足要求的处理逻辑。
4)案例实操
(1)需求
将该List(1,2,3,4,5,6,“test”)中的Int类型的元素加一,并去掉字符串。
(2)实操
//方法一:
List(1,2,3,4,5,6,"test").filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int] + 1).foreach(println)
//方法二:
List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(println)