1. 模式匹配介绍
模式匹配是检查某个值(value)是否匹配某一个模式的机制,一个成功的匹配同时会将匹配值解构为其组成部分。它是Java中的switch语句的升级版,同样可以用于替代一系列的 if/else 语句。
1.1 语法
一个模式匹配语句包括一个待匹配的值,match关键字,以及至少一个case语句。
match 对应 Java 里的 switch,但是写在选择器表达式之后。即: 选择器 match {备选项}。
match 表达式通过以代码编写的先后次序尝试每个模式来完成计算,只要发现有一个匹配的case,剩下的case不会继续匹配。
import scala.util.Random
val x: Int = Random.nextInt(10)
x match {
case 0 => "zero"
case 1 => "one"
case 2 => "two"
case _ => "other"
}
// 上述代码中的val x是一个0到10之间的随机整数,
// 将它放在match运算符的左侧对其进行模式匹配,
// match的右侧是包含4条case的表达式,
// 其中最后一个case _表示匹配其余所有情况,在这里就是其他可能的整型值。
2. 模式匹配种类
2.1 通配符匹配
即通配符匹配 _ ,匹配所有的情况
/**
* 通配符匹配
* @param x
* @return
*/
def m1(x: Any) = x match {
case List(0, _, _) => "匹配 0 元素开头的list"
case List(1, _*) => "匹配 1 元素开头的list"
case Vector(1, _*) => "匹配 1 元素开头的vector"
case m: Map[_, _] => m.toString
case _ => "Unknown"
}
println(m1(List(0,1,2))) //匹配 0 元素开头的list
println(m1(List(1,2))) //匹配 1 元素开头的list
println(m1(Vector(1,2,3))) //匹配 1 元素开头的vector
2.2 常量匹配
def m2(x:Any) = x match {
case 0 => println("zero")
case true => println("true")
case "hello" => println("you said 'hello'")
case Nil => println("an empty List")
case _ => println("unknow")
}
println(m2("hello")) //you said 'hello'
println(m2(true)) //true
2.3 变量匹配
- 常量匹配很简单,即case后跟的都是常量
- 变量匹配需要注意,case后跟的是match里面的临时变量,而不是其他变量名:
def m3(str:String) = str match {
case "a" => 1
case "b" =>"b"
case _ => -1
}
println(m1("a")) //1
println(m1("b")) //b
3 match {
case i => println("i=" + i) // 这里i是模式变量(临时变量),就是3
}
val a = 20
20 match { case a => 1 } // 1, a是模式变量,不是10
//为了使用变量a,必须用`a`:
20 match { case `a` => 1; case b => -1 } // -1,`a`是变量10
//或者用大写的变量
val A = 10
20 match { case A => 1; case b => -1 } // -1,大写A是变量10
2.4 构造函数匹配
构造器模式不只检查顶层对象是否一致,还会检查对象的内容是否匹配内层的模式。由于额外的模式自身可以形成构造器模式,因此可以使用它们检查到对象内部的任意深度。
object ConstructorPattern {
def main(args: Array[String]): Unit = {
val a1 = Cat("多啦A梦", "白色")
val s1 = what(a1)
println(s1) //a cat name is 多啦A梦,color is 白色
val a2 = Dog("多啦B梦", "白色", 500)
val s2 = what(a2)
println(s2) //a dog name is 多啦B梦,color is 白色 ,age is 500
}
def what(animal: Animal): String = animal match {
case Cat(name: String, color: String) => s"cat's is $name,color $color"
case Dog(name: String, color: String, age: Int) => s"dog's is $name,color $color ,age $age"
}
}
2.5 集合类型匹配
/**
* 匹配数组
*/
def m5(arr: Array[Int]) = arr match {
case Array(1, x, y) => println("匹配以1 开头,有三个元素的数组");
case Array(0) => println("匹配只有 0 这个元素的数组");
case Array(0, _*) => println("匹配以0 开头任意多个元素的数组");
case arr if arr.length == 2 => println("length = 2")
case _ => println("unknow")
}
println(m5(Array(1, 2, 3))) //匹配以1 开头,有三个元素的数组
println(m5(Array(0, 2, 3))) //匹配以0 开头任意多个元素的数组
println(m5(Array(0))) //匹配只有 0 这个元素的数组
println(m5(Array(9, 0))) //length = 2
/**
* 匹配序列list
*/
def m5_1(list: List[Int]) = list match {
case 5 :: Nil => println("匹配只有 5 这个元素的序列");
case x :: y :: Nil => println("匹配只有两个元素的序列");
case x :: tail => println("匹配任意多个元素的数组");
case list if list.last == 1 => println()
case _ => println("unknow")
}
println(m5_1(List(5,6,7))) //匹配任意多个元素的数组
println(m5_1(List(0,4))) //匹配只有两个元素的序列
2.6 元组类型匹配
/**
* 匹配元组list
*/
def m6(tuple: Any) = tuple match {
case (x, y, 7) => println("匹配有三个元素并且以7 结尾的元组");
case (2, x, y) => println("匹配以2 开头有三个元素的元组");
case _ => println("unknow")
}
println(m6((1,2,7))) //匹配有三个元素并且以7 结尾的元组
println(m6((2,2,0))) //匹配以2 开头有三个元素的元组
2.7 类型匹配
match 可以很简单地匹配数据类型(不需要isInstanceOf[T]):
为了让匹配更加具体,可以使用模式守卫,也就是在模式后面加上if
/**
* 匹配类型
*/
def m7(x: Any): String = x match {
case x: String => x
case x: Int if x > 5 => x.toString //带if守卫条件的匹配
case _ => "unknow"
}
println(m7("hello")) //hello
println(m7(9)) // 9
println(m7(2)) // unknow ,(虽然2满足Int类型, 但是不满足守卫条件"大于5",所以往下匹配)
def m7_1(v: Any) = v match {
case null => "null"
case i: Int => i * 100
case s: String => s
case _ => "others"
}
// 注意:上面case中的i、s都叫模式变量
println(m7_1(null)) // "null"
println(m7_1(5)) // 500
println(m7_1("hello")) // "hello"
println(m7_1(3.14)) // "others"
注意:自定义类型如果也要匹配,需要用case class
3. 模式匹配基本应用
3.1 match..case 和 switch..case的区别
// Java写法
switch(n) {
case(1): ...; break;
case(2): ...; break;
default: ...;
}
// Scala写法 ,每次结束不用写break , _相当于default
def m(n:String) = n match {
case "a" | "b" => ...
case "c" => ...
case _ => ...
}
3.2 命令行参数解析例子
/** Basic command line parsing. */
object Main {
var verbose = false // 记录标识,以便能同时对-h和-v做出响应
def main(args: Array[String]) {
for (a <- args) a match {
case "-h" | "-help" => println("Usage: scala Main [-help|-verbose]")
case "-v" | "-verbose" => verbose = true
case x => println("Unknown option: '" + x + "'") // 这里x是临时变量
}
if (verbose) println("How are you today?")
}
}
3.3 使用case的递归函数
def fac1(n:Int):Int = n match {
case 0 => 1
case _ => n * fac1( n - 1 )
}
fac1(5) //120
// 同
def fac2: Int => Int = {
case 0 => 1
case n => n * fac2( n - 1 )
}
fac2(5) //120
// 同 使用尾递归
def fac3: (Int,Int) => Int = {
case (0,y) => y
case (x,y) => fac3(x-1, x*y)
}
fac3(5,1) // 120
// 同 reduceLeft
def fac4(n:Int) = 1 to n reduceLeft( _ * _ )
implicit def foo(n:Int) = new { def ! = fac4(n) }
5! // 120
// 同
def fac5(n:Int) = (1:BigInt) to n product
fac5(5) // 120
3.4 case..if条件匹配
(1 to 20) foreach {
case x if (x % 15 == 0) => printf("%2d:15n\n",x)
case x if (x % 3 == 0) => printf("%2d:3n\n",x)
case x if (x % 5 == 0) => printf("%2d:5n\n",x)
case x => printf("%2d\n",x)
}
// 或者
(1 to 20) map (x=> (x%3,x%5) match {
case (0,0) => printf("%2d:15n\n",x)
case (0,_) => printf("%2d:3n\n",x)
case (_,0) => printf("%2d:5n\n",x)
case (_,_) => printf("%2d\n",x)
})
3.5 try..catch..finally
var f = openFile()
try {
f = new FileReader("inputPath")
} catch {
case ex: FileNotFoundException => // Handle missing file
case ex: IOException => // Handle other I/O error
} finally {
f.close()
}