Scala模式匹配--样例类--密封样例类--偏函数
Scala模式匹配--样例类--密封样例类--偏函数
模式匹配
等价于java的switch case
val c = '+'
c match{
case '+' => println(111)
case '-' => println(222)
case _ => println(0)
}
匹配常量
//匹配值,没有break
val x :Char= '+'
//万一xx存在这种可能case '-'=>"100" case _=>0.0F
//val xx :Int=x match{
val xx :Any=x match{ //Any所有类的超类
case '+'=>100
case '-'=>"100"
case _=>0.0F
}
守卫条件
val x = c match{
case '+' => 1
case '-' => -1
case _ if Character.isDigit(c) => 'N'
//case _if x.isDigit=>0.0F 它如果是数字
case _ => 'X'
}
val x :Char='9'
val xx:Any=x match{
case '+'=>100
case '-'=>"100"
case _if x.isDigit=>println("is number") //if x.isDigit这里就是守卫条件
case _=>println(x)
}
//守卫可以是任何Boolean条件。
注意模式总是从上往下进行匹配。如果带守卫的这个模式不能匹配,则捕获所有的模式(case_)会被用来尝试进行匹配。
使用变量:如果case关键字后面跟着一个变量名,那么匹配的表达是会被赋值给那个变量。
val str = "hello"
str(0) match{
case '+' => println(1)
case '-' => println(2)
case ch => println(ch)
}
常量问题,大写会识别成常量,小写是变量,如果让小写也是常量,使用``标出
val str = "hello"
val ch = 'h'
str(0) match{
case '+' => println(1)
case '-' => println(2)
case `ch` => println(`ch`)
}
类型匹配
val x:Any = "abc"
x match{
case a:Int => println(1)
case a:String => println(2)
case a:Float => println(3)
case _ => println(0)
}
匹配数组
val arr=Array(0) // 100 is Int
val arr=Array(0,1) //2=>x:0
val arr=Array(0,1,2,3) //default
arr match{
//精确匹配
case Array(0)=>println(1)
//匹配两个元素
case Array(x,y)=>println(2+"=>x: "+x)
//匹配0为首个元素
case Array(0,_*)=>println(3)
case _=>println("default")
}
val arr = Array[Int](1,2,3)
arr match{
//case Array(0) => println(1) //精确匹配(0)
case Array(x,y) => println(2) //匹配2个元素
case Array(0,_*) => println(3) //匹配0开始
case _ => println(4) //
}
总结:第一个模式匹配包含0的数组。第二个模式匹配任何带有两个元素的数组,并且将这两个元素分别绑定到变量x和y中。第三个表达式匹配任何以零开始的数组。
匹配集合(List)
val list = 0::1::2::Nil
list match{
case 0::Nil => println(1) //0
case x::y::Nil => println(2) //x,y
case 0::tail => println(3) //0,...
case _ => println(4)
}
val list=1::2::Nil //2 =>x: 1
val list=0::1::2::Nil //List(1,2)
list match{
//精确匹配
case 0::Nil=>println(1)
//匹配两个元素
case x::y::Nil=>println(2+"=>x: "+x)
//匹配0为首个元素
case 0::tail=>println(tail)
case _=>println("default")
}
匹配元组
val t = (1,2)
t match{
case (0,_) => println(1)
case (y,0) => println(2)
case _ => println(3)
}
val t=(0,1) //100
val t=(1,0) //200 必须符合case里面的条件才行
t match{
case(0,_)=>println("100")
case(x,0)=>println("200")
case _ =>println("default")
}
val(x,y)=(1,2)
同时把x定义为1,把y定义为2.这对于使用那些返回对偶的函数而言很有用
val (q,r)=BigInt(10)/%3
/%方法返回包含商和余数的对偶,而这两个值分别被变量q和r捕获到。
提取器unapply / unapplySeq
val arr = Array(1,2,3)
arr match{
case Array(x,y,z)=> printf("x=%d,y=%d,z=%d" , x , y ,z)
case _ => println("0")
}
scala> val t=<1,2>
t:<Int,Int>=<1,2>
scala> val<x,y>=t //可以同时把t给抽出来
x:Int=1
y:Int=2
scala> val<x,y>=t
//反向抽取
//如果元素很多,可以用t给抽出来
scala>val t=<1,"tom",12,"henan">
t:<Int,String,Int,String>=<1,tom,12,henan>
scala>val<id,name,age,addr>=t
id:Int=1
name:String=tom
age:Int=12
addr:String=henan
多个变量声明模式
val (x,y,z) = (1,2,3)
x = 1
y = 2
z = 3
//模式匹配的变量声明模式
val t=(1,"tom",12,"henan") //这个就是元组
val (id,name,age,addr)=t
println(id)
[源码解析]
Tuple4 extends(继承)Product4
object Product4{
def unapply[T1,T2,T3,T4] (x:Product4[T1,T2,T3,T4]):Option[Product4[T1,T2,T3,T4]]=Some(x)
option类型(Some(类) , None(单例对象))
}
------------------
//同时提取商和余数
val (a,b)=BigInt(5) /% 3
val arr=Array(1,2,3,4)
val Array(x,y,_*)=arr //for循环的map迭代
println(x+","+y)//1,2
val map=Map(1->"tom1",2->"tom2")
//相当于反向抽取(常见的形式)
for((k,y)<-map ){
println(k+"="+v)
}
抽取前两个元素依次赋值
val Array(x,y,_*) = Array(1,2,3,4,5)
val map = Map(1->"tom1" , 2->"tom2",3->"tom3")
for((k,_) <- map) println(k)
for(t <- map) println(t._1)
//模式匹配的变量声明模式
样例类
.样例类是一种特殊的类,它们经过优化以被用于模式匹配。内置实现了很多便捷的方法,比如apply/unapply/serilizable/compare/toString
abstact classAmount //一个抽象类 //定义了两个样例类 case class Dollar(value:Double)extends Amount case class Currency(value: Double,unit: String)extends Amount //样例类必须要有属性 //通过普通类定义一个子类,它和样例类有啥区别 object CaseClassDemo{ def main(args:Array[String]):Unit={ abstract class Amount //定义样例类 //sealed(密封),样例子类和样例父类如果不加sealed是可以定义在不同的scala文件里的 如果加了sealed就必须跟它的父类定义在同一个Scala文件中 //case class Dollar(cnt:Int)extends Amount样例子类,sealed的优点是:在一个scala文件中可以找到它所有的sealed子类,就不会存在在其它的包里面或文件夹里面。 sealed case class Dollar(cnt:Int)extends Amount case class RenmiYuan(cnt:Int)extends Amount //定义一个样例对象 case object NoMoney extends Amount //可以实例化对象(快速构建对象) //apply val d1=Dollar(10) //apply val r1=RenmiYuan(100) //打印 --因为里面已经重新了toString方法 println(d1) //Dollar(10) println(r1) //RenmiYuan(100) //快速抽出里面的属性 //unapply val Dollar(x)=d1 //10 //unapply val RenmiYuan(y)=r1 //100 println(x) //Serializable(scala)继承java里面的,就是java里面的串行化接口实现 package scala trait Serializable extends Any with java.io.Serializable -------------样例对象模式匹配----------------- //给样例对象变量,是否可以模式匹配,这里用到的都是unapply反解析 val m=Dollar(300) //300美元 val m:Amount=RenmiYuan(300)//300人民币 val m:Amount=NoMoney //NoMoney本来就是样例对象 //模式匹配,判断它是美元还是人民币 //对样例实例进行模式匹配 m match{ case Dollar(x)=>println(x+"美元") case RenmiYuan(y)=>println(y+"人民币") case NoMoney=>println("没钱") } } }
密封样例类
必须将样例子类和父类定义在一个scala文件中。
方便在一个scala文件中找到所有的子类。不允许在其他地方定义
子类。和匹配模式配合使用,非常方便。
//sealed 密封
sealed abstract class Dog
case class Jing8(var name:String) extends Dog
case class Shapi(var name:String) extends Dog
[典型的密封样例类源码]
//Option密封抽象类
sealed abstract class Option[+A]extends Product with Serializable
它的两个子类 None Some
//None子对象(样例对象)
case object None extends Option[Nothing]{
def isEmpty=true
def get=throw new NoSuchElementException("None.get")
}
//Some(样例类)
final case class Some[+A](x: A)extends Option[A]{
def isEmpty=false
def get=x
}
偏函数(PartialFunction)
被包在花括号内的一组case语句是一个偏函数---一个并非对所有输入值都有定义的函数。它是PartialFunction[A,B]类的一个实例。(A是参数类型,B是返回类型。)该类有两个方法:apply方法从匹配到模式技术函数值,而isDefinedAt方法在输入至少匹配其中一个模式时返回true.
模式匹配有一个缺点就是代码需要重用,假如很多地方需要用到模式匹配。就需要反复的写这些东西,完全可以{}包起来的一组case给它封装成一个对象,这个对象就叫做偏函数。偏函数还有一种情况就是,它没有完全把所有的情况罗列完整。它只是一部分,或者是若干情况。
偏函数的作用:偏函数就是对模式匹配代码进行重用,以备于在其它的地方还可以利用这段代码执行模式匹配的动作。
//偏函数,对模式匹配的封装,重用模式匹配的代码
object PartialFunctionDemo{
def main(args:Array[String]):Unit={
val ch:Char='+'
val r=ch match{
case '+'=>1
case '-'=>-1
case _=>0
}
println(r) //1
//定义一个偏函数对象(类),代码就是把case放进去
val f:PartialFunction[Char,Int]={
case '+'=>1
case '-'=>-1
case _=>0
}
//isDefinedAt是否定义了这种情况,它是判断是否有没有定义这种情况的处理
println(f.isDefinedAt('+')) //1
println(f.isDefinedAt('9')) //true 如果把//case _=>0注释点就 false
//调用这个
println(f.apply('+')) //1
//可以简写
println(f('-')) //-1
println(f('9'))//注释掉 case _=>0,则抛异常(MatchError)没有定义这个数
}
}