模式匹配
- 唱量模式
常量模式仅匹配自身,任何字面量都可用做常量。
def matchTest(x:Int):String=x match{
case 1=>"one"
case 2=>"two"
case _=>"many"
}
- 变量模式
变量模式类似于通配模式,可以匹配任意对象,不过与通配符不同的是,Scala将变量绑定在匹配的对象上,随后可以使用该变量操作对象。
val expr=10
expr match {
case 0=>println("zero")
case x=>println(x) //变量模式
}
- 构造器模式
构造器模式检查对象是否为该名称的样本类实例,并提取构造参数绑定到指定变量中。
1.样例类的模式匹配
样例类中,自动实现了 unapply()方法(也称提取器),用于将对象分解为用
以匹配模式的片段,即该方法接受一个实例对象,返回最初创建它所用的参数。
//定义一个样例类
case class Student(name:String,age:Int)
def main(args: Array[String]): Unit = {
//定义一个样例类的模式匹配
def matchTest04(x:Student)=x match {
case Student(name,19) =>println(name+" 1111的年龄是19")
case Student("Tom",age) => println("Tom的年龄是"+age)
case Student(name,age)=>println(name,age)
case _ =>println("no matches")
}
val stu1 = Student("zhangsan",19)
// matchTest04(stu1)
matchTest04(Student("Tom",19))
matchTest04(Student("lisi",20))
2.非样例类的匹配模式
普通类也可以使用模式匹配,前提是实现了提取器。
提取器返回的是 Option 类型,该类型有两种形式:
➢ Some(x):表示有值,x 是实际值
➢ None:表示无值
Some 和 Node 均是 Option 的子类。
class Student(_name:String,_age:Int) {
var name=_name
var age=_age
}
object Student{
def unapply(arg: Student): Option[(String, Int)] ={
if(arg==null) None else Some(arg.name,arg.age) //实现提取器
}
}
def matchTest(x:Student)=x match {
//应用提取器 Student.unapply(x)
case Student(n,a) => println(n,a)
}
matchTest(new Student("Jason",19)) //输出:(Jason,19)
- 类型模式
def matchTest3(x: Any): String = x match {
case x:Int => "Int"
case x:String => "String"
case _ => "Any"
}
matchTest3(3.0) // Any
matchTest3(1) // Int
- 模式守卫
模式守卫与 for 循环守卫用法类似,用于限制模式的匹配规则。守卫可以是引用模式中的任意布尔表达式,如果有守卫,只有在守卫返回 true 时才匹配成功。
def matchTest2(x: Int): String = x match {
case i if i==1 => "one"
case i if i==2 => "two"
case _ => "many"
}
matchTest2(3) // many
matchTest2(1) // one
偏函数
偏函数是只对函数定义域的一个子集进行定义的函数。举例来说,List 中可能存在不同类型的元素:List(1,2,3,“four”),假设现在需要对数字进行加 1 操作:
List(1,2,3,"four").map(x=>if(x.isInstanceOf[Int]) x.asInstanceOf[Int]+1 else x)
//输出 List(2,3,4,"four")
对于普通函数,需要考虑所有输入参数的情况,也就是说,对于函数 A => B,它对 A 类型的所有值都有意义;而偏函数PartialFunction[A, B]仅对 A 类型的部分值有意义。
使用偏函数实现上面需求的代码如下:
val pf:PartialFunction[Any, Int]={case x:Int=>x+1}
List(1,2,3,"four").collect(pf) //输出 List(2,3,4)
可以发现,对于偏函数 pf 来说,对 Any 类型所有值仅处理了符合要求(x:Int)的 Int 数据,返回 Int 类型。通常偏函数不需单独定义,类似下面用法:
List(1,2,3,"four").collect({case x:Int=>x+1}) //输出List(2,3,4)
List(1,2,3,"four").collect{case x:Int=>x+1} //可省略函数小括号
其中,“{ case pattern=> ??? }”表达式是一个整体,包括外面的大括号。这里 case 后面的写法与模式匹配完全相同,可以为不同的匹配模式。为了方便描述,统称为 case 语句、或者函数字面量。
可以认为,一个 case 语句就是一个独立的匿名函数,如果有一组 case 语句的话,从效果上看,构建出的这个匿名函数会有多种不同的参数列表,每一个case 对应一种参数列表,参数是 case 后面的变量声明,其值是通过模式匹配赋
予的。例如:
List(1,2,3,"four").map{case x:Int=>x+1;case x:String=>x} //类型模式
List(1,2,3).map{case 1=>"one";case 2=>"two";case 3=>"three"} //常量模式
实际上,case 语句返回的是一个特质 PartialFunction(继承自(A=>B)),其本质还是一个函数(scala.Function1),所以当成函数用没有一点问题。
但是反过来,普通函数当成偏函数使用就不可以
case 语句是创建 PartialFunction 的便捷方式,原始自定义创建 PartialFunction方式如下。
//自定义一个偏函数
val pf1: PartialFunction[Any, Int] = new PartialFunction[Any, Int] {
override def isDefinedAt(x: Any): Boolean = {
//判断是不是int类型,如果是int,我们就对它进行处理
if (x.isInstanceOf[Int]) true else false
}
override def apply(v1: Any): Int = {
v1.asInstanceOf[Int] + 10
}
}
val list = List(1,2,3,4,"five")
PartialFunction 特质规定了两个要实现的方法:apply 和 isDefinedAt。
1)isDefinedAt 用来告知调用方这个偏函数接受参数的范围,可以是类型也可以是值,在我们这个例子中我们要求这个 inc 函数只处理 Int 型的数据。
2)apply 方法用来描述对已接受的值如何处理,在我们这个例子中,我们只是简单的把值+1,注意,非 Int 型的值已被 isDefinedAt 方法过滤掉了,所以不用担心类型转换的问题
正则表达式
- 使用 String 类的 matches()方法
"1123".matches("[a-zA-Z0-9]{4}")
- 模式匹配
先定义规则,再通过模式匹配的方式来匹配
//模式匹配
//使用“.r”方法可使任意字符串变成一个Regex实例
val re: Regex = "[a-zA-Z][0-9][a-zA-Z] [0-9][a-zA-Z][0-9]".r
"L3R 6M2" match {
case re(x)=>println("valid zip-code:"+x)
case x=>println("invalid zip-code:"+x)
}
3.使用 Regex(scala.util.matching.Regex)的提取器进行模式匹配
findFirstMatchIn() 返回第一个匹配(Option[Match])
findAllMatchIn() 返回所有匹配结果(Regex.Match)
findAllIn() 返回所有匹配结果(String)
import scala.util.matching.Regex
val numberPattern: Regex = "[0-9]".r
numberPattern.findFirstMatchIn("awesomepassword") match {
case Some(_) => println("Password OK") //匹配成功
case None => println("Password must contain a number") //未匹配
}
- 捕获分组
识别“name:Jason,age:19,……”中的键值对
import scala.util.matching.Regex
val studentPattern:Regex="([0-9a-zA-Z-#() ]+):([0-9a-zA-Z-#() ]+)".r
val input="name:Jason,age:19,weight:100"
for(patternMatch<-studentPattern.findAllMatchIn(input)){
println(s"key: ${patternMatch.group(1)} value: ${patternMatch.group(2)}")
}
- 字符串替换
//search
val nums = "[0-9]+".r.findAllIn("123 Main Street Suite 2012")
nums.next // -> 123
nums.next // -> 2012
//replace
"[0-9]+".r.replaceFirstIn("234 Main Street Suite 2034", "567") //234->567
"[0-9]+".r.replaceAllIn("234 Main Street Suite 2034", "567") //234、2034->567
- 在字符串中查找模式
val date = """(\d\d\d\d)-(\d\d)-(\d\d)""".r
"2014-05-23" match {
case date(year, month, day) => println(year,month,day)
}
"2014-05-23" match {
case date(year, _*) => println("The year of the date is " + year)
}
"2014-05-23" match {
case date(_*) => println("It is a date")
}