Skip to content

Latest commit

 

History

History
274 lines (201 loc) · 6.75 KB

专题1——模式匹配.md

File metadata and controls

274 lines (201 loc) · 6.75 KB

专题 1 —— 模式匹配

一、Case Class

定义一组 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(234
  • 对偶模式,用于对偶操作。该函数接受两个列表,通过对相应元素的相加构造出一个新的列表
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 Classes 与 Option

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) }