Scala 中的模式匹配类似于 Java 中的 switch 语法,但是 scala 从语法中补充了更多的功能,所以更加强大
模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有 case 都不匹配,那么会执行 case _分支,类似于 Java 中 default 语句
package com.michael.learn.day06
object TestPattern {
def main(args: Array[String]): Unit = {
var a: Int = 10
var b: Int = 20
var operator: Char = '+'
var result = operator match {
case '+' => a +b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "illegal"
}
println(result) // 30
}
}
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫
package com.michael.learn.day06
object TestGuardPattern {
def main(args: Array[String]): Unit = {
def abs(x:Int) = x match {
case i:Int if i >= 0 => i
case j:Int if j < 0 => -j
case _ => "type illegal"
}
println(abs(-5)) // 5
}
}
Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等
需要进行类型判断时,可以使用scala提供的 isInstanceOf[T]和 asInstanceOf[T],也可使用模式匹配实现同样的功能
package com.michael.learn.day06
object TestMatchClass {
def main(args: Array[String]): Unit = {
// erase generics
println(describe(List(1,2,3,4,5))) // List
// keep generics with array
println(describe(Array(1,2,3,4,5,6))) // Array[Int]
println(describe(Array("abc"))) // something else [Ljava.lang.String;@457e2f02
}
def describe(x: Any) = x match {
case i: Int => "Int"
case s: String => "String hello"
case m: List[_] => "List"
case c: Array[Int] => "Array[Int]"
case someThing => "something else " + someThing
}
}
scala 模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为 0 的数组
package com.michael.learn.day06
object TestMatchArray {
def main(args: Array[String]): Unit = {
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90)))
{ // 对一个数组集合进行遍历
val result = arr match {
case Array(0) => "0" //匹配 Array(0) 这个数组
case Array(x, y) => x + "," + y //匹配有两个元素的数组,然后将将元素值赋给对应的 x,y
case Array(0, _*) => "以 0 开头的数组" //匹配以 0 开头和数组
case _ => "something else"
}
print("reslult = " + result + ";") // reslult = 0;reslult = 1,0;reslult = 以 0 开头的数组;reslult = something else;reslult = something else;reslult = hello,90;
}
}
}
package com.michael.learn.day06
object TestMatchList {
def main(args: Array[String]): Unit = {
for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1,0, 0), List(88))){
val result = list match {
case List(0) => "0" //匹配 List(0)
case List(x, y) => x + "," + y //匹配有两个元素的 List
case List(0, _*) => "0 ..."
case _ => "something else"
}
print(result+";") // 0;1,0;0 ...;something else;something else;
}
}
}
package com.michael.learn.day06
object TestMatchList2 {
def main(args: Array[String]): Unit = {
val list: List[Int] = List(1, 2, 5, 6, 7)
list match {
case first :: second :: rest => println(first + "-" + second + "-" + rest)
case _ => println("something else")
} } // 1-2-List(5, 6, 7)
}
package com.michael.learn.day06
object TestMatchTuple {
def main(args: Array[String]): Unit = {
//对一个元组集合进行遍历
for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
val result = tuple match {
case (0, _) => "0 ..." //是第一个元素是 0 的元组
case (y, 0) => "" + y + "0" // 匹配后一个元素是 0 的对偶元组
case (a, b) => "" + a + " " + b
case _ => "something else" //默认
}
print(result + ";") // 0 ...;10;1 1;something else;
}
}
}
package com.michael.learn.day06
object TestMatchTuple2 {
def main(args: Array[String]): Unit = {
//特殊的模式匹配 1 打印元组第一个元素
for (elem <- Array(("a", 1), ("b", 2), ("c", 3))) {
print(elem._1+";") // a;b;c;
}
println()
for ((word,count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
print(word+";") // a;b;c;
}
println()
for ((word,_) <- Array(("a", 1), ("b", 2), ("c", 3))) {
print(word+";") // a;b;c;
}
println()
for (("a",count) <- Array(("a", 1), ("b", 2), ("c", 3))) {
print(count+";") // 1;
}
println("--------------")
//特殊的模式匹配 2 给元组元素命名
var (id,name,age): (Int, String, Int) = (100, "zs", 20)
println((id,name,age)) // (100,zs,20)
println("--------------")
//特殊的模式匹配 3 遍历集合中的元组,给 count * 2
var list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3))
//println(list.map(t => (t._1, t._2 * 2)))
println(
list.map{
case (word,count)=>(word,count*2) } ) // List((a,2), (b,4), (c,6))
var list1 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3)))
println(
list1.map{
case (groupkey,(word,count))=>(word,count*2) } ) // List((a,2), (b,4), (c,6))
}
}
package com.michael.learn.day06
object TestMatchUnapply {
def main(args: Array[String]): Unit = {
val user: User = User("michael", 18)
val result = user match {
case User("michael", 18) => "yes"
case _ => "no"
}
println(result) // yes
}
}
object User{
def apply(name: String, age: Int): User = new User(name, age)
def unapply(user: User): Option[(String, Int)] = {
if (user == null)
None
else
Some(user.name, user.age)
}
}
class User(val name: String, val age: Int)
case class Person (name: String, age: Int)
package com.michael.learn.day06
object TestMatchUnapply2 {
def main(args: Array[String]): Unit = {
val user: User2 = User2("michael", 18)
val result = user match {
case User2("michael", 18) => "yes"
case _ => "no"
}
println(result) // yes
}
}
case class User2(name: String, age: Int)
package com.michael.learn.day06
object TestMatchVariable {
def main(args: Array[String]): Unit = {
val (x, y) = (1, 2)
println(s"x=$x,y=$y") // x=1,y=2
val Array(first, second, _*) = Array(1, 7, 2, 9)
println(s"first=$first,second=$second") // first=1,second=7
val Person(name, age) = Person("michael", 18)
println(s"name=$name,age=$age") // name=michael,age=18
}
}
case class Person(name: String, age: Int)
package com.michael.learn.day06
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("--------- separator -------------")
//遍历 value=0 的 k-v ,如果 v 不是 0,过滤
for ((k, 0) <- map) {
println(k + " --> " + 0) // B->0
}
println("--------- separator -------------")
//if v == 0 是一个过滤的条件
for ((k, v) <- map if v >= 1) {
println(k + " ---> " + v) // A->1 和 c->3
}
}
}
偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为 List[Int],而我们需要的是第一个元素是 0 的集合,这就是通过模式匹配实现的。
val second: PartialFunction[List[Int], Option[Int]] = {
case x :: y :: _ => Some(y)
}
注:该偏函数的功能是返回输入的 List 集合的第二个元素
上述代码会被 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)
}
}
偏函数不能像 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 方法为参数不满足要求的处理逻辑
package com.michael.learn.day06
object TestPartialFunc {
def main(args: Array[String]): Unit = {
// 将该 List(1,2,3,4,5,6,"test")中的 Int 类型的元素加一,并去掉字符串
val list = List(1,2,3,4,5,6,"test")
val list1 = list.map {
a => a match {
case i:Int => i + 1
case s:String => s + 1
}
}
println(list1) // List(2, 3, 4, 5, 6, 7, test1)
println(list1.filter(_.isInstanceOf[Int])) // List(2, 3, 4, 5, 6, 7)
List(1,2,3,4,5,6,"test").filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int] + 1).foreach(print) // 234567
println()
List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(print) // 234567
}
}