Scala模式匹配(matchcase)
Scala的match表达式类似于其他语⾔中的switch语句,它可以提供给你在多个备选项中做选择。基本上match表达式可以让你使⽤任意的模式(pattern)做选择。
1. 按值匹配
def valueMatch(fruit: String) {
fruit match {
case "apple" => print("apple")
case "banana" => print("banana")
case "orange" => print("orange")
}
}
match表达式检测参数fruit,如果是字符串"apple"就打印"apple",如果是"banana",就打印"banana",如此类推。
2. 按对象类型匹配
def classMatch(obj: Any): Unit = {
obj match {
case x: Int => print(x + " is int")
case x: String => print(x + " is string")
case x: List[Int] => print(x + " is list")
}
}
按类型匹配时,case语法为:case ⾃定义变量名: 要匹配的类类型 => 执⾏语句。如果不需要在执⾏语句中使⽤这个变量,可以把⾃定义变量名使⽤下划线(_)代替。需要注意的时当期望匹配⽬标类型时
List, Tuple等含有泛型参数的⾼阶类型时,由于泛型擦除机制的缘故,⽆法正确识别参数类型T:
classMatch(List("hello", "ok"))
java switch case string运⾏结果:
List(hello, ok) is list
3. 通配模式
下划线(_)可以⽤来表⽰匹配任意的对象。在上⾯的按值匹配的例⼦中,如果传⼊的参数是 "hello",程序会报匹配异常错误,正确的做法是加⼊下划线(_)匹配,从⽽忽略掉程序中你不关⼼的部分:
def valueMatch(fruit: String) {
fruit match {
case "apple" => print("apple")
case "banana" => print("banana")
case "orange" => print("orange")
case _ => print("unknown")
}
}
4. 构造器模式
与Java的Switch语句⽐,匹配表达式还有⼀些重要的差别。其中之⼀是任意类型的常量,或其他什么东西,都能当成Scala⾥⽐较⽤的样本(Case),⽽不只是Java的case语句⾥⾯的整数类型和枚举常量。另⼀个区别是在每个备选项的最后并没有break。取⽽代之的是,break是隐含的,也就是说,不允许从上⼀个备选项落⼊到下⼀个⾥⾯去的情况发⽣。然⽽,与Java的switch相⽐,最显著的差别或许是match表达式能产⽣值。在上⾯的例⼦⾥,match表达式的每个备选项不但可以通过打印输出值,还可以只⽣成返回值⽽不打印:
def simpleMatch(fruit: String) {
val name = fruit match {
case "apple" => "apple"
case "banana" => "banana"
case "orange" => "orange"
case _ => "unknown"
}
name
}
2. 样本类
带有case修饰符的类被称为样本类(case class),样本类例⼦如下:
abstract class Animal
case class Cat(name: String, kind: String) extends Animal
case class Dog(name: String, kind: String) extends Animal
上⾯的类都没有类结构体,Scala可以去掉围绕空结构体的花括号,因此class c与class c {} 相同。case修饰符可以让scala编译器⾃动为你的类添加⼀些句法上的便捷设定。
⾸先,它会添加与类名⼀致的⼯⼚⽅法。构造对象时不再需使⽤new关键字:
val cat = Cat("tom", "Persian")
第⼆个便捷设定是样本类参数列表中的所有参数隐式获得了val前缀,因此它被当作字段维护:
val cat = Cat("tom", "Persian")
print(cat.name)
第三,是编译器为你的类添加了⽅法toString、hashCode 和 equals的⾃然实现,它们能够打印,哈希和⽐较由类及(递归地得到)器所有参数组成的整棵树。因为Scala⾥的==始终直接转到equals,这也就意味着样本类的元素⼀直是在做结构化的⽐较。
3. 通配模式
通配模式( _ )匹配任意对象:
val cat = Cat("tom", "Persian")
cat match {
case Cat("tom", "Persian") => print("matched!")
case _ => print("not matched!")
}
matched!
val cat = Cat("tom", "Persian")
cat match {
case Cat("jetty", "Persian") => print("matched!")
case _ => print("not matched!")
}
not matched!
通配模式还可以⽤来忽略对象中你并不关⼼的部分。⽐如说,上⼀个例⼦实际上并不关⼼猫的名字和类别,只是检查是否是猫:
val cat = Cat("tom", "Persian")
cat match {
case Cat(_, _) => print("matched!")
case _ => print("not matched!")
}
matched!
match语句中的case 类型⼀定要是被⽐较对象的同类型或者⼦类型,例如下⾯的语句⽆法通过编译:
val animal = Dog("erha", "Husky")
animal match {
case Cat(_, _) => print("the animal is a cat!")
case _ => print("the animal is unknown ")
}
Error:(57, 12) constructor cannot be instantiated to expected type;
found : Cat
required: Dog
case Cat(_, _) => print("the animal is a cat!")
这是因为变量animal已由Scala的⾃动类型推导确定为Dog类型,⽆法实例为Cat类型,需要⼿动指定变量animal为Animal类型:
val animal: Animal = Dog("erha", "Husky")
animal match {
case Cat(_, _) => print("the animal is a cat!")
case _ => print("the animal is unknown ")
}
the animal is unknown
4. 常量模式
常量模式仅匹配⾃⾝。任何字⾯量都可以⽤作常量,任何的val或单例对象也可以⽤作常量。例如,单例对象Nil只匹配空列表模式。
scala> def describe(x: Any) = x match {
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list"
case _ => "something else"
}
5. 变量模式
变量模式类似于通配符,可以匹配任意对象。不过与通配符不同的地⽅在于,Scala把变量绑定在匹配的对象上。因此之后你可以使⽤这个变量操作对象。
val animal: Animal = Dog("erha", "Husky")
animal match {
case Cat(_, _) => print("the animal is a cat!")
case other => print("the animal is: " + other)
}
the animal is: Dog(erha,Husky)
6. 序列模式
你也可以像匹配样本类那样匹配如List或Array这样的序列类型。下⾯的例⼦展⽰了检查开始于零的三元素列表的模式:
expr match {
case List(0, _, _) => print("found it!")
case _ =>
}
如果你想匹配⼀个不指定长度的序列,可以使⽤_*作为最后的元素。这种模式能匹配序列中零到任意数量的元素:
expr match {
case List(0, _*) => print("found it!")
case _ =>
}
7. 模式守卫
模式守卫接在模式之后,开始于if,后接Boolean表达式:
val animal: Animal = Dog("erha", "Husky")
animal match {
case Dog(name, kind) if kind.equals("Husky") =>
case other => print("the animal is: " + other)
}
8. 封闭类
⼀旦你写好了模式匹配,你就需要确认已经考虑到了所有的情况。例如,定义了⼀个抽象类和n个⼦样本类,你想要在模式匹配语句中考虑所有⼦类型组合。通常,这在Scala是不可能的,因为新的样本⼦类可以在任意新的⽂件内被定义。可选⽅案就是让样本类的超类被封闭(sealed)。封闭类除了类定义所在⽂件之外不能再添加任何⼦类:
sealed abstract class Animal
case class Cat(name: String, kind: String) extends Animal
case class Dog(name: String, kind: String) extends Animal
9. Option类型
Scala为可选值定义了⼀个名为Option的标准类型。这种值可以有两种形式。可以是Some(x)的形式,其中x是实际值,或者可以是None对象,代表缺失的值。
def show(x: Option[String]) = x match {
case Some(x) => x
case None => "?"
}
上⾯代码背后的原理还是Scala的类型匹配,因为Some类和None类都是Option的⼦类,Scala源码中定义如下:
final case class Some[+A](x: A) extends Option[A] {
def isEmpty = false
def get = x
}
case object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("")
}
10. 模式在变量定义中
在定义val或var的任何时候,都可以使⽤模式替代简单的标识符。例如,你可以使⽤模式拆分元组并把其中的每个值分配给变量:
val myTuple = ("abc", 123)
val (string, number) = myTuple
val dog = Dog("erha", "Husky")
val Dog(name, kind) = dog
11. for表达式⾥的模式
for((country, city) <- capitals) {
println("the capital of " + country + " is " + city)
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论