Scala
中的模式匹配
类似于
Java
中的
switch
语法
模式匹配语法中,采用
match
关键字声明,每个分支采用
case
关键字进行声明,当需
要匹配时,会从第一个
case
分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹
配不成功,继续执行下一个分支进行判断。如果所有
case
都不匹配,那么会执行
case _
分支,
类似于
Java
中
default
语句。
var a: Int = 10
var b: Int = 20
var operator: Char = 'd'
var result = operator match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "illegal"
}
println(result)
(
1
)如果所有
case
都不匹配,那么会执行
case _
分支,类似于
Java
中
default
语句,
若此时没有
case _
分支,那么会抛出
MatchError
。
(
2
)每个
case
中,不需要使用
break
语句,自动中断
case
。
(
3
)
match case
语句可以匹配任何类型,而不只是字面量。
(
4
)
=>
后面的代码块,直到下一个
case
语句之前的代码是
作为一个整体执行
,可以
使用
{}
括起来,也可以不括。
模式守卫
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫。
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))
匹配常量
def describe(x: Any) = x match {
case 5 => "Int five"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
匹配类型
需要进行类型判断时,可以使用前文所学的
isInstanceOf[T]
和
asInstanceOf[T]
,也可使
用模式匹配实现同样的功能。
package com.qihang.bigdata.spark.core
object Test {
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
}
def main(args: Array[String]): Unit = {
//泛型擦除
println(describe(List(1, 2, 3, 4, 5)))
//数组例外,可保留泛型
println(describe(Array(1, 2, 3, 4, 5, 6)))
println(describe(Array("abc")))
}
}
//List
//Array[Int]
//something else [Ljava.lang.String;@6debcae2
匹配数组
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 Array(x,1,y) => "中间为1的三元数组"
case _ => "something else"
}
println("result = " + result)
}
//_ 为任意一个元素 _*为任意个任意元素
匹配列表
方式1:
//list 是一个存放 List 集合的数组
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"
}
println(result)
方式2:
list match {
case first :: second :: rest => println(first + "-" + second + "-" + rest)
case _ => println("something else")
//第一个元素::第二个元素::其他元素(可以是空列表 Nil)
//当元素为1个时 case _
匹配元组
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" //默认
}
println(result)
扩展用法
val (x,y) = (10,"www")
val List(first,second,_*) = List(21,21,34,35)
val fir :: sce :: rest = List(21,34,23,43)
for推导式模式匹配
val list:List[(String,Int)] = List(("a",12),("b",34))
for(elem <- list){
elem._1 elem._2
}
for( (x,y) <- list ){
x y
}
//只遍历key或value
for( (word, _) <- list){
println(word)
}
//筛选
for(("a",count) <- list){
count
}
匹配对象及样例类
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)
}
}
object TestMatchUnapply {
def main(args: Array[String]): Unit = {
val user: User = User("zhangsan", 11)
val result = user match {
case User("zhangsan", 11) => "yes"
case _ => "no"
}
println(result)
}
}
➢
val user = User("zhangsan",11)
,该语句在执行时,实际调用的是
User
伴生对象中的
apply
方法,因此不用
new
关键字
就能构造出相应的对象。
➢
当将
User("zhangsan", 11)
写在
case
后时
[case User("zhangsan", 11) => "yes"]
,会默
认调用
unapply
方法
(
对象提取器
)
,
user
作为
unapply
方法的参数
,
unapply
方法
将
user
对象的
name
和
age
属性提取出来,与
User("zhangsan", 11)
中的属性值进行
匹配
➢
case
中对象的
unapply
方法
(
提取器
)
返回
Some
,且所有属性均一致,才算匹配成功
,
属性不一致,或返回
None
,则匹配失败。
➢
若只提取对象的一个属性,则提取器为
unapply
(obj:Obj):
Option[
T
]
若提取对象的多个属性,则提取器为
unapply
(obj:Obj):
Option[
(T1,T2,T3…)
]
若提取对象的可变个属性,则提取器为
unapplySeq
(obj:Obj):
Option[
Seq[T]
]
样例类
case class User(name: String, age: Int)
object TestMatchUnapply {
def main(args: Array[String]): Unit = {
val user: User = User("zhangsan", 11)
val result = user match {
case User("zhangsan", 11) => "yes"
case _ => "no"
}
println(result)
}
}
(
1
)语法:
case
class
Person (name: String, age: Int)
(
2
)说明
○1 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中
自动提供了一些常用的方法,如
apply
、
unapply
、
toString
、
equals
、
hashCode
和
copy
。
○2 样例类是为模式匹配而优化的类,因为其默认提供了
unapply
方法,因此,样例
类可以直接使用模式匹配,而无需自己实现
unapply
方法。
○3 构造器中的每一个参数都成为
val
,除非它被显式地声明为
var
(不建议这样做)
case class User(name: String, age: Int)
object TestMatchUnapply {
def main(args: Array[String]): Unit = {
val user: User = User("zhangsan", 11)
val result = user match {
case User("zhangsan", 11) => "yes"
case _ => "no"
}
println(result)
}
}
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
}
}
}
偏函数
将问题分解多种情况的子问题,分别实现。
val second: PartialFunction[List[Int], Option[Int]] = {
case x :: y :: _ => Some(y)
}
代码会被
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
方法
为参数不满足要求的处理逻辑。
来源:
尚硅谷