0%

scala-模式匹配

模式匹配是检查某个值(value)是否匹配某一个模式的机制,一个成功的匹配同时会将匹配值解构为其组成部分。它是Java中的switch语句的升级版,同样可以用于替代一系列的 if/else 语句。

scala的模式匹配是其非常出彩的一个点。

模式匹配语法

一个模式匹配语句包括一个待匹配的值,match关键字,以及至少一个case语句。 match 对应 Java 里的 switch,但是写在选择器表达式之后。即: 选择器 match {备选项}。 match 表达式通过以代码编写的先后次序尝试每个模式来完成计算,只要发现有一个匹配的case,剩下的case不会继续匹配。

1
2
3
4
5
6
7
8
9
10
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 _表示匹配其余所有情况,在这里就是其他可能的整型值。
1
2
3
4
5
6
7
def matchTest(x: Int): String = x match {
case 1 => "one"
case 2 => "two"
case _ => "other"
}
matchTest(3) // other
matchTest(1) // one
这个match表达式是String类型的,因为所有的情况(case)均返回String,所以matchTest函数的返回值是String类型。

模式匹配种类

通配符匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 通配符匹配
* @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

常量匹配

1
2
3
4
5
6
7
8
9
10
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

变量匹配

1
2
3
4
5
6
val a = 20
val b = 30
20 match { case a => 1 } // 1, a是模式变量,不是10
//为了使用变量a,必须用`a`:
20 match { case `a` => 1; case `b` => -1 } // -1,`a`是变量10
//或者用大写的变量

构造函数匹配

构造器模式不只检查顶层对象是否一致,还会检查对象的内容是否匹配内层的模式。由于额外的模式自身可以形成构造器模式,因此可以使用它们检查到对象内部的任意深度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sealed trait Animal
case class Cat(name: String, color: String) extends Animal
case class Dog(name: String, color: String, age: Int) extends Animal

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"
}
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

集合类型匹配

数组匹配

1
2
3
4
5
6
7
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")
}
序列匹配
1
2
3
4
5
6
7
8
9
10
11
def m5_1(list: List[Int]) = list match {
case 5 :: Nil => println("匹配只有 5 这个元素的序列")
case x :: y :: Nil => println("匹配只有两个元素的序列")
case list if list.last == 1 => println("结尾是1的列表")
case x :: tail => println("匹配任意多个元素的数组")
case _ => println("unknow")
}

m5_1(List(5)) //匹配只有 5 这个元素的序列
m5_1(List(1,2)) //匹配只有两个元素的序列
m5_1(List(1,2,3,4,5,1)) //结尾是1的列表

tuple类型匹配

1
2
3
4
5
6
7
8
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 开头有三个元素的元组

类型匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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"

模式匹配的应用

递归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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

模式匹配和二叉数遍历

利用scala的模式匹配和递归可以轻松实现二叉树的遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* 1
* 2 3
* 4 5 6
* 7 8
*/
object bTreeExample {

abstract class BinaryTree

case class Node(value: Int,
left: BinaryTree = null, right: BinaryTree = null) extends BinaryTree

def traverse(t: BinaryTree): Unit = {
t match {
case null => Unit
case Node(v, left, right) => traverse(left);println(v);traverse(right) // 中序
//case Node(v, left, right) => println(v); traverse(left); traverse(right) //前序
//case Node(v, left, right) => traverse(left);traverse(right);println(v) //后序
}
}

def BFS(t: (List[Node], List[Int])): (List[Node], List[Int]) = t match {
case (Nil, res) => (Nil, res)
case (lB, res) => {
val tmp = lB.map { case Node(v, null, null) => (Nil, v)
case Node(v, left, null) => (List(left.asInstanceOf[Node]), v)
case Node(v, null, right) => (List(right.asInstanceOf[Node]), v)
case Node(v, left, right) => (List(left.asInstanceOf[Node], right.asInstanceOf[Node]), v)
}
BFS((tmp.flatMap(_._1), res ++ tmp.map(_._2)))
}
}

def DFS(t:(List[Node], List[Int])): (List[Node], List[Int]) =t match{
case (Nil, res) => (Nil, res)
case (lB, res) => {
val top = lB.head
val left = if(top.left == null) Nil else List(top.left.asInstanceOf[Node])
val right = if(top.right == null) Nil else List(top.right.asInstanceOf[Node])
DFS(left ++ right ++ lB.tail, res ++ List(top.value))
}
}

def main(args: Array[String]): Unit = {
val t = Node(1, Node(2, Node(4), Node(5, Node(7), Node(8))), Node(3, null, Node(6)))
//traverse(t)
val res = DFS((List(t), List.empty[Int]))
println(res._2) // List(1, 2, 4, 5, 7, 8, 3, 6)

//val res = DFS((List(t), List.empty[Int]))
//println(res._2) // List(1, 2, 4, 5, 7, 8, 3, 6)
}

}