模式匹配
在前面的章节已经讲了Scala中的简单模式匹配。模式匹配是一个表达式,因此它会导致一个值,该值可能被分配或返回。例如:
44 match { case 44 => true // 如果匹配了44,则结果为true case _ => false // 否则,结果是false }
也可以匹配字符串,例如:
"过期商品" match { case "过期商品" => .45 case "未过期商品" => .77 case _ => 1.0 }
可用模式匹配实现java中switch语句功能:
val status = 500 val message = status match { case 200 => "ok" case 400 => { println("ERROR - 调用的服务不正确") "error" } case 500 => { println("ERROR - 服务器遇到了问题") "error" } } val day = "MON" val kind = day match { case "MON" | "TUE" | "WED" | "THU" | "FRI" => "工作日" case "SAT" | "SUN" => "周末" }
Scala强大的模式匹配机制,可以应用在switch语句、类型检查以及“析构”等场合。
def swithOps: Unit ={ var sign = 0 val ch: Char = '+' ch match { case '+' => sign = 1 case '-' => sign = -1 case _ => sign = 0 } println("sign===> " + sign) } swithOps
上面代码中,case _模式对应于switch语句中的default,能够捕获剩余的情况。如果没有模式能匹配,会抛出MatchError。而且不像常见的switch语句,在一种模式匹配之后,需要使用break来声明分支不会进入下一个分支,在scala中并不需要这种break语句。
另外,match是表达式,不是语句,所以是有返回值的,故可将代码简化:
sign = ch match { case '+' => 1 case '-' => -1 case _ => 0 } println("sign=====> "+ sign)
Scala中的任何表达式都是有返回值的,模式匹配也不例外,我们可以直接获取对应的返回值进行操作。如果不写case _的操作,匹配不上的话,会抛出相关异常:scala.MatchError。
def caseOps2: Unit = { val ch = '1' val sign = ch match { case '+' => 1 case '-' => 0 // case _ => 2 } println(sign) }
一般情况下也就是将模式匹配当做java中的switch case来进行使用的。
如果在case关键字后跟着一个变量名,那么匹配的表达式会被赋值给那个变量。case _ 是这个特性的一个特殊情况,变量名是_。
// 将要进行匹配的值,赋值给case后面的变量,我们可以对变量进行各种操作 def caseOps4: Unit = { "Hello, wordl" foreach(c => println( c match { case ' ' => "space" case ch => "Char: " + ch } )) } caseOps4
基本模式匹配
下面的代码演示了基本模式匹配的使用:
def printNum(int: Int) { int match { case 0 => println("Zero") case 1 => println("One") case _ => println("more than one") } } printNum(0) printNum(1) printNum(2) def fibonacci(in: Int): Int = in match { case 0 => 0 case 1 => 1 case n => fibonacci(n - 1) + fibonacci(n - 2) } fibonacci(10) def fib1(in: Int): Int = in match { case 0 | -1 | -2 => 0 case 1 => 1 case n => fibonacci(n - 1) + fibonacci(n - 2) } // 条件守护 def fib2(in: Int): Int = in match { case n if n <= 0 => 0 case 1 => 1 case n => fib2(n - 1) + fib2(n - 2) }
模式匹配和在case子句中使用if表达式
可以通过在case语句中添加if表达式来模拟OR子句。
object demo { def main(args: Array[String]): Unit = { println("\n模式匹配和在case子句中使用if表达式") // val fruitType = "水果布丁" // val fruitType = "杨桃" val fruitType = "随便" val tasteLevel = fruitType match { case fruit if fruit.contains("水果") || fruit.contains("布丁") => "肯定很美味" case "杨桃" => "尝一尝" case _ => "可以尝一尝" } println(s"${fruitType}的味道 = $tasteLevel") } }
匹配元组
object demo { def main(args: Array[String]): Unit = { val one = ("张三", "男", 25000.00) val two = ("李四", "男", 30000.00) val three = ("小美", "女", 18000.00) val allList = List(one, two, three) // 元组匹配 val priceOfPlainDonut = allList.foreach { tuple => { tuple match { case ("张三", gender, salary) => println(s"张三, 性别=$gender, 薪资=$salary") case d if d._1 == "张三" => println(s"${d._1}, 性别=${d._2}, 薪资=${d._3}") case _ => None } } } } }
执行以上代码,输出结果如下:
张三, 性别=男, 薪资=25000.0
以上模式匹配更优雅的写法:
object demo { def main(args: Array[String]): Unit = { val one = ("张三", "男", 25000.00) val two = ("李四", "男", 30000.00) val three = ("小美", "女", 18000.00) val allList = List(one, two, three) // 更优雅的写法 allList.foreach { case ("张三", gender, salary) => println(s"张三, 性别=$gender, 薪资=$salary") case d if d._1 == "张三" => println(s"${d._1}, 性别=${d._2}, 薪资=${d._3}") case _ => None } } }
匹配Option类型
object demo { def main(args: Array[String]): Unit = { val title:Option[String] = Some("老板") // val title:Option[String] = None title match{ case Some(a) => println(s"title是$a。") case None => println(s"没有title,屌丝一个。") } } }
匹配Any类型
下面的代码演示了如何将模式匹配应用于Any类型的数据:
val anyList= List(1, "A", 2, 2.5, 'a') for (m <- anyList) { m match { case i: Int => println("Integer: " + i) case s: String => println("String: " + s) case f: Double => println("Double: " + f) case other => println("other: " + other) } }
使用模式匹配可以代替isInstanceOf和asInstanceOf来进行使用。相比使用isInstanceOf来判断类型,使用模式匹配更好。
def caseOps5: Unit = { def typeOps(x: Any): Int = { val result = x match { case i: Int => i case s: String => Integer.parseInt(s) case z: scala.math.BigInt => Int.MaxValue case c: Char => c.toInt case _ => 0 } result } println(typeOps("12345") == 12345) } caseOps5
可以通过模式匹配进行类型匹配。例如:
object demo06 { def main(args: Array[String]): Unit = { println("\n按类型匹配模式") val priceOfFruit: Any = 2.50 val priceType = priceOfFruit match { case price: Int => "Int" case price: Float => "Float" case price: Double => "Double" case price: String => "String" case price: Boolean => "Boolean" case price: Char => "Char" case price: Long => "Long" } println(s"水果价格类型 = $priceType") } }
执行以上代码,输出结果如下:
按类型匹配模式 水果价格类型 = Double
下面的代码同样演示了怎样通过模式匹配判断变量的数据类型:
def test2(in: Any) = in match { case s: String => "字符串,长度是" + s.length case i: Int if i > 0 => "自然整数" case i: Int => "整数" case a: AnyRef => a.getClass.getName case _ => "null" }
匹配List类型
也可以使用模式匹配来匹配数组、列表和元组:
def caseOps6: Unit = { val arr = Array(0, 1) arr match { // 匹配只有一个元素的数组,且元素就是0 case Array(0) => println("0") // 匹配任何带有两个元素的数组,并将元素绑定到x和y case Array(x, y) => println(x + " " + y) // 匹配任何以0开始的数组 case Array(0, _*) => println("0 ...") case _ => println("something else") } } caseOps6
下面的代码演示了如何将模式匹配应用于List类型的数据:
val x = 1 val rest = List(2,3,4) x :: rest // 模式匹配:即可以比较,也可以提取值 (x :: rest) match { // 注意创造和匹配之间的对称性 case Nil => 0 case x :: rest => println(x); println(rest) }
下面的代码使用模式匹配对所有奇数求和:
def sumOdd(in: List[Int]): Int = in match { case Nil => 0 case x :: rest if x % 2 == 1 => x + sumOdd(rest) case _ :: rest => sumOdd(rest) // 忽略第1个元素 } val list1 = List(2,3,4) sumOdd(list1)
下面的代码匹配List中任意数量的元素:
def noPairs[T](in: List[T]): List[T] = in match { case Nil => Nil case a :: b :: rest if a == b => noPairs(a :: rest) // 如果前两个元素相同,排除连续重复的元素 case a :: rest => a :: noPairs(rest) } noPairs(List(1,2,3,3,3,4,1,1))
模式匹配既可以匹配常量,也可以提取信息:
def ignore(in: List[String]): List[String] = in match { case Nil => Nil case _ :: "ignore" :: rest => ignore(rest) case x :: rest => x :: ignore(rest) }
使用类测试/强制转换机制查找List[Any]中的所有字符串:
def getStrings(in: List[Any]): List[String] = in match { case Nil => Nil case (s: String) :: rest => s :: getStrings(rest) case _ :: rest => getStrings(rest) }
模式匹配与case class
模式匹配也可以对两个case class进行匹配:
case class Person(name: String, age: Int, valid: Boolean) val p = Person("张三", 45, true) // 也可以 val m = new Person("李四", 24, true) def older(p: Person): Option[String] = p match { case Person(name, age, true) if age > 35 => Some(name) case _ => None } older(p).get older(p).getOrElse("匿名") // older(m).get // 会出现异常 older(m).getOrElse("匿名")
样例类(case class)的模式匹配
下面这个示例代码,演示了如何匹配多个case object:
trait Command case object Start extends Command case object Go extends Command case object Stop extends Command case object Whoa extends Command def executeCommand(cmd: Command) = cmd match { case Start | Go => println("starting") case Stop | Whoa => println("stopping") case default => println("You gave me: " + default) // 可访问default值 } executeCommand(Start) executeCommand(Whoa)
模式匹配作为参数
模式匹配可作为参数传递给其他函数或方法,编译器会将模式匹配编译为函数:
val list = List("aa",123,"ss",456,"dd") list.filter(a => a match { case s: String => true case _ => false }) // 上面的代码可简化为 list.filter { case s: String => true case _ => false }