定义一组 case class
abstract class Expr
case class Var(name:String) extends Expr
case class Number(num:Double) extends Expr
case class UnOp(operator:String, arg:Expr) extends Expr
case class BinOp(operator:String,left:Expr,right:Expr) extends Expr
使用 case 这个关键字,Scala 编译器会自动为你定义的类生成一些成员。
首先编译器会为你生成一个同名的对象构造器(Factory method),也就是可以使用 Var("x")来创建一个实例
其次,Scala 编译器为 case class的构造函数的参数创建以参数名为名称的属性。比如,Val 的类的参数 name:String 可以直接通过 .name 访问
第三,编译器为 case class 构造了更自然的 toString、hashCode 和 equals 实现
最后一点,Scala 编译器为 case class 添加了一个 copy 方法。copy 方法允许你在创建 case 类的新实例时,只给出与原对象不同部分的参数,这一点对于大一些的 case 类非常有用。
val p1 = new Point(x=3.3, y=4.4)
val p2 = p1.copy(y=6.6)
case 类的 模式匹配
def simplifyTop(expr :Expr) :Expr = expr match {
case UnOp("-",UnOp("-",e))=>e
case BinOp("+",e,Number(0))=>e
case BinOp("*",e,Number(1))=>e
case _ => expr
}
simplifyTop(UnOp("-",UnOp("-",Var("x"))))
① 通配模式(没有返回值,表示除匹配值外都忽略)
def simplifyBinOp(expr :Expr) = expr match {
case BinOp(op,left,right) => println( expr + " is a binary operation")
case _ =>
}
② 常量模式
单例也可用做常量模式,比如 Nil
def describe(x:Any) =x match {
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list"
case _ => "something else"
}
也可以引用之前已经定义的变量(使用反引号将其包围)
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)
}
}
checkY(100)
③ 变量模式
将输入绑定到一个变量中,在 => 可以对这个变量进行相关操作
def isZero(x:Any) = x match{
case 0 => "zero"
case somethingElse => "not zero:" + somethingElse
}
除了独立的变量模式外,还可以绑定对象中的部分内容作为一个整体赋值给变量
expr match {
case UnOp("abs",e @ UnOp("abs",_)) => e
case _ =>
}
注:如果 @ 右边匹配了的话,会将该处代表的值的绑定到变量 e
④ 构造器模式
检查输入与case构造器是否匹配
def simplifyBinOp(expr :Expr) = expr match {
case BinOp("+",e,Number(0)) => println(" a deep match")
case _ =>
}
构造模式,包含
- 序列模式(匹配 List)
//3个元素,首元素是 0
List(0,2,4) match{
case List(0,x,_) => print ("found it " +x )
case _ =>
}
//1个或多个元素,首元素是 0
List(0,2,4) match{
case List(0,_*) => print ("found it " )
case _ =>
}
//直接匹配元素。s 表示List中的第一个元素, rest 表示剩余的元素
def listToString(list: List[String]): String = list match {
case s :: rest => s + " " + listToString(rest)
case Nil => ""
}
- 多元组模式
def tupleDemo(expr: Any) = expr match {
case (a, 5, c) => print("matched " + a + ":" + ":" + c)
case _ =>
}
tupleDemo(2,3,4)
- 对偶模式,用于对偶操作。该函数接受两个列表,通过对相应元素的相加构造出一个新的列表
def addPairwise(a: List[Int], b:List[Int]):List[Int] = (a,b) match {
case (Nil, _) => Nil
case (_, Nil) => Nil
case (Cons(h1,t1), Cons(h2,t2)) => Cons(h1+h2, addPairwise(t1,t2))
}
⑤ 类型模式
def generalSize(x:Any) = x match{
case s:String => s.length
case m:Map[_,_] => m.size
case _ => -1
}
//异常处理,通过类型匹配执行不同的处理
try {
val f = new FileReader("/home/hadoop/input.txt")
} catch {
case ex: FileNotFoundException => //handle missing file
case ex: IOException => //handle other I/O error
}
// 由于运行时类型擦除的问题
// 通常无法在运行时判定泛型是否匹配。在 scala中 数组例外
def isStringArray(x:Any) = x match{
case a:Array[String]=>"yes"
case _ => "no"
}
isStringArray(Array(1,2,3)) //output: no
def simplifyAdd(e:Expr) = e match{
case BinOp("+",x,y) if x==y => BinOp("*",x,Number(2))
case _ =>
}
注:就功能而言,相对于生成子类,修饰模式显得更为灵活,于是我们可以给某个对象添加一些功能,而不是为整个类。
sealed 用于保证被修饰的 trait 、 class 等只能在当前文件里面被继承。
使用 case 类匹配时,无法考虑到所有的情况,可使用 sealed 关键字,编译器会自动推断是否包含全部子类。 为简洁起见,Scala 支持使用标注(annotation)的方法暂时取消编译器检查模式定义是否完备,为变量添加 @unchecked 标注后,编译器不再给出警告
sealed abstract class Expr
case class Var(name:String) extends Expr
case class Number(num:Double) extends Expr
case class UnOp(operator:String, arg:Expr) extends Expr
case class BinOp(operator:String,left:Expr,right:Expr) extends Expr
def describe(e:Expr) :String =(e: @unchecked) match{
case Number(_) => "a number"
case Var(_) => "a variable"
}
Scala 语言中包含一个标准类型—— Option 类型。它代表可选值, Option 类型的值可以有两个可能的值:一个为 some(x) 其中 x 为有效值,另外一个为 None 对象,代表空值。
val capitals = Map("France"->"Paris", "Japan"->"Tokyo","China"->"Beijing")
capitals get "France" // Option[String] = Some(Paris)
capitals get "North Pole" //Option[String] = None
def show(x:Option[String]) = x match{
case Some(s) => s
case None => "?"
}
show (capitals get "France") //output: Paris
1.赋值解构
// tuple
val tp = ("lisi", 13)
val (name, age) = tp
println(name) //output: lisi
// case class
val myL = L("lisi", 23)
val L(name, age) = myL
println(name) //output: lisi
//for-expression
val capitals = Map("France"->"Paris", "Japan"->"Tokyo","China"->"Beijing")
for((country,city) <- capitals)
println("The captical of " + country + " is " + city)
2.匿名函数与偏函数中使用 case 表达式
偏函数在Map 中的使用
val stateCapitals = Map("Alabama" -> "Montgomery", "Alaska" -> "Juneau", "Wyoming" -> "Cheyenne")
val lengths = stateCapitals map { kv => (kv._1, kv._2.length) }
val caps = stateCapitals map { case (k, v) => (k, v.toUpperCase) }