7.6 模式匹配

在前面的章节已经讲了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
}
© 版权声明
THE END
喜欢就支持一下吧
点赞101赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

夸夸
夸夸
还有吗!没看够!
取消
昵称表情代码图片

    暂无评论内容