在Scala的模式匹配中,可以使用类型、通配符、序列、正则表达式,甚至可以深入获取对象的状态。
val bools = Seq(true, false)
for (bool <- bools) {
bool match {
case true => println("Got heads")
case false => println("Got tails")
}
}
Got heads Got tails
bools: Seq[Boolean] = List(true, false)
for {
x <- Seq(1, 2, 2.7, "one", "two", "four")
} {
var str = x match {
case 1 => "int 1"
case i: Int => "other int: " + i
case d: Double => "a double: " + x
case "one" => "string one"
case s: String => "other string: " + s
case unexpected => "unexpected value: "+ unexpected
}
println(str)
}
int 1 other int: 2 a double: 2.7 string one other string: two other string: four
unexpected匹配任意输入,x的值被赋给unex这个变量。由于未给出任何类型说明,unexpected的类型被推断为Any,起到default语句的作用。
在简单的匹配表达式中,建议使用@switch注解,如果模式匹配不能编译成tableswitch或者lookupswitch,该注解将报错。tableswitch或者lookupswitch具有更好的性能,比决策树的方式更加高效,可以直接跳到要匹配的结果上。
import scala.annotation.switch
import scala.annotation.switch
val i = 1
val x = (i: @switch) match {
case 1 => "One"
case 2 => "Two"
case _ => "Other"
}
i: Int = 1 x: String = "One"
// 该情况使用scalac编译将报错
val i = 1
val Two = 2 // added
val x = (i: @switch) match {
case 1 => "One"
case Two => "Two" // replaced the '2'
case _ => "Other"
}
i: Int = 1 Two: Int = 2 x: String = "One"
Scala使用tableswitch优化具有一定争议,不想@tailrec那么通用。tableswitch优化需要满足几个条件:
// 有时,我们也可以使用Map来代替简单的switch
val monthNumberToName = Map(
1 -> "January",
2 -> "February",
3 -> "March",
4 -> "April",
5 -> "May",
6 -> "June",
7 -> "July",
8 -> "August",
9 -> "September",
10 -> "October",
11 -> "November",
12 -> "December"
)
val monthName = monthNumberToName(4)
println(monthName) // prints "April"
April
monthNumberToName: Map[Int, String] = Map( 5 -> "May", 10 -> "October", 1 -> "January", 6 -> "June", 9 -> "September", 2 -> "February", 12 -> "December", 7 -> "July", 3 -> "March", 11 -> "November", 8 -> "August", 4 -> "April" ) monthName: String = "April"
在被匹配或提取的值中,编译器假定以大写字符开头的为类型名,以小写字母开头的为变量名。
def checkY(y: Int) = {
for {
x <- Seq(99, 100, 101)
} {
val str = x match {
case `y` => "found y!"
case i: Int => "int: " + i
}
println(str)
}
}
defined function checkY
checkY(100)
int: 99 found y! int: 101
在case子句中,以小写字母开头的标识符被认为是用来提取待匹配的新变量。如果需要引用之前已经定义的变量时,应使用反引号将其包围。
trait Command
case object Start extends Command
case object Go extends Command
case object Stop extends Command
case object Whoa extends Command
defined trait Command defined object Start defined object Go defined object Stop defined object Whoa
def executeCommand(cmd: Command) = cmd match {
case Start | Go => "start" // or use start()
case Stop | Whoa => "stop"
}
defined function executeCommand
executeCommand(Stop)
res11: String = "stop"
除了上面对整数、字符串等对象的匹配之外,模式匹配还可以对序列、元组、样本类(case class)、映射等进行匹配。
case class Person(firstName: String, lastName: String)
case class Dog(name: String)
defined class Person defined class Dog
def echoWhatYouGaveMe(x: Any): String = x match {
// constant patterns
case 0 => "zero"
case true => "true"
case "hello" => "you said 'hello'"
case Nil => "an empty List"
// sequence patterns
case List(0, _, _) => "a three-element list with 0 as the first element"
case List(1, _*) => "a list beginning with 1, having any number of elements"
case Vector(1, _*) => "a vector starting with 1, having any number of elements"
// tuples
case (a, b) => s"got $a and $b"
case (a, b, c) => s"got $a, $b, and $c"
// constructor patterns
case Person(first, "Alexander") => s"found an Alexander, first name = $first"
case Dog("Suka") => "found a dog named Suka"
// typed patterns
case s: String => s"you gave me this string: $s"
case i: Int => s"thanks for the int: $i"
case f: Float => s"thanks for the float: $f"
case a: Array[Int] => s"an array of int: ${a.mkString(",")}"
case as: Array[String] => s"an array of strings: ${as.mkString(",")}"
case d: Dog => s"dog: ${d.name}"
case list: List[_] => s"thanks for the List: $list"
case m: Map[_, _] => m.toString
// the default wildcard pattern
case _ => "Unknown"
}
defined function echoWhatYouGaveMe
// trigger the constant patterns
println(echoWhatYouGaveMe(0))
println(echoWhatYouGaveMe(true))
println(echoWhatYouGaveMe("hello"))
println(echoWhatYouGaveMe(Nil))
// trigger the sequence patterns
println(echoWhatYouGaveMe(List(0,1,2)))
println(echoWhatYouGaveMe(List(1,2)))
println(echoWhatYouGaveMe(List(1,2,3)))
println(echoWhatYouGaveMe(Vector(1,2,3)))
// trigger the tuple patterns
println(echoWhatYouGaveMe((1,2))) // two element tuple
println(echoWhatYouGaveMe((1,2,3))) // three element tuple
// trigger the constructor patterns
println(echoWhatYouGaveMe(Person("Melissa", "Alexander")))
println(echoWhatYouGaveMe(Dog("Suka")))
// trigger the typed patterns
println(echoWhatYouGaveMe("Hello, world"))
println(echoWhatYouGaveMe(42))
println(echoWhatYouGaveMe(42F))
println(echoWhatYouGaveMe(Array(1,2,3)))
println(echoWhatYouGaveMe(Array("coffee", "apple pie")))
println(echoWhatYouGaveMe(Dog("Fido")))
println(echoWhatYouGaveMe(List("apple", "banana")))
println(echoWhatYouGaveMe(Map(1->"Al", 2->"Alexander")))
// trigger the wildcard pattern
println(echoWhatYouGaveMe("33d"))
zero true you said 'hello' an empty List a three-element list with 0 as the first element a list beginning with 1, having any number of elements a list beginning with 1, having any number of elements a vector starting with 1, having any number of elements got 1 and 2 got 1, 2, and 3 found an Alexander, first name = Melissa found a dog named Suka you gave me this string: Hello, world thanks for the int: 42 thanks for the float: 42.0 an array of int: 1,2,3 an array of strings: coffee,apple pie dog: Fido thanks for the List: List(apple, banana) Map(1 -> Al, 2 -> Alexander) you gave me this string: 33d