(Scala五) 模式匹配

//########################### 5.1  match case #################################
/**
  * scala 中的match不需要break终止关键字,碰到满足的第一个case就会停止往下执行
  * match可以有返回值
  */
//var 变量:类型= 值
var ch: Char = '+'
var sign = -1
ch match {
  case '-' => sign = -1
  case '+' => sign = 1
  case _ => sign = 0
}

sign

//match可以有返回值
var flat = ch match {
  case '-' => -1
  case '+' => 1
  case _ => 0
}

//########################### 5.2 match中的if守卫 #################################
/**
  * if作为case的守卫语句
  * case 和 if 的关系是 逻辑与(&&),不满足就会往下走
  */
var ch2 = '3'
var digit = ch match {
  case _ if Character.isDigit(ch2) => ch2
  case _ => -1
}

/**
  * if 作为for的守卫语句
  * 一个for循环可以支持多个if语句,以空格、回车字符分割
  * 多个if语句之间的关系是逻辑与(&&);
  */
val items = for (i <- 1 to 10 if i % 2 == 0 if i > 5) yield i;

//########################### 5.3  模式中的变量 #################################
/**
  * case 后跟变量ch , str(i) 会赋值给ch
  */
val str = "+=3!"
for (i <- str.indices) {
  var sign = 0
  var digit = 0

  str(i) match {
    case '+' => sign = 1
    case '-' => sign = -1
    case ch if Character.isDigit(ch) => digit = Character.digit(ch, 10)
    case _ =>
  }
  println(str(i) + " " + sign + " " + digit)
}

//########################### 5.4  类型模式 #################################
/**
  * 通过这种方式,避免了使用isInstanceOf 和 asInstanceOf
  * 在匹配类型的时候,必须要设置一个变量名
  */
for (obj <- Array(42, "42", BigInt(42), BigInt, 42.0)) {
  val result = obj match {
    //case 变量:类型 => 操作
    case x: Int => x
    case s: String => s.toInt
    case _: BigInt => Int.MaxValue
    case BigInt => -1
    case _ => 0
  }
  println(result)
}

/**
  * 匹配操作发生在运行期,所以不能对比一个泛型类型,
  * 但是可以使用通配符.但是对预算数组来说,类型信息保存完好,可以完全匹配Array[T]
  */
for (obj <- Array(Map("Fred" -> 42), Map(42 -> "Fred"), Array(42), Array("Fred"))) {
  val result = obj match {
    //    case m: Map[String, Int] => "It's a Map[String,Int]"
    case ms: Map[_, _] => "It's a Map"
    case a: Array[Int] => "It's an Array[Int]"
    case a: Array[String] => "It's an Array[String]"
    case a: Array[_] => "It's an array of something other than Int"
    case _ => "others ..."
  }
  println(result)
}

//########################### 5.5  匹配数组、列表和元组 #################################
/**
  * 匹配数组
  */
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0))) {
  val result = arr match {
    case Array(0) => "0"
    case Array(x, y) => x + " " + y
    case Array(0, _*) => "0..."
    case _ => "something else"
  }
  println(result)
}

/**
  * 匹配列表
  */
for (lst <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0))) {
  val result = lst match {
    case 0 :: Nil => "0"
    case x :: y :: Nil => x + " " + y
    case 0 :: tail => "0..."
    case _ => "something else"
  }
  println(result)
}


/**
  * 匹配元组
  */
for (pair <- Array((0, 1), (1, 0), (1, 1))) {
  val result = pair match {
    case (0, _) => "0..."
    case (y, 0) => y + " 0"
    case _ => "neither is 0"
  }
  println(result)
}


//########################### 5.6  提取器 #################################
/**
  * 几何元素通过匹配绑定到变量,这样的操作叫做"析构"
  * 模式匹配如何匹配数组、列表和元组的,这个背后的机制叫做"提取器"
  * 一个对象如果带有从对象中提取之的unapply和unapplySeq方法,apply方法接受构造参数生成对象,而unapply方法接受对象,提取值,是一个反向操作.
  *
  * scala中 update,apply,unApply,unApplySeq四个方法用的时候可以省略掉方法名
  * apply可以省略,可以让你在有参构造时不写new关键字
  */
val a :: b = List(0, 0, 0, 0)

val arr = Array(0, 1)
val Array(x, y) = arr
val Array(z, _*) = arr
arr match {
  case Array(0, w) => w
}

val aaa = Array.unapplySeq(arr)
arr.toIndexedSeq

/**
  * unapply的内部实现机理
  */
//自定义unapply
object Name {
  //不提供unapply时,编译报错:Cannot resolve method Name.unapply
  def unapply(input: String): Option[(String, String)] = {
    val pos = input.indexOf(" ")
    if (pos == -1) None
    else Some((input.substring(0, pos), input.substring(pos + 1)))
  }
}

val Name(ccc, ddd) = "zuo shuai"

/**
  * unapplySeq 内部实现机理
  */
object NameSeq {
  def unapplySeq(input: String): Option[Seq[String]] =
    if (input.trim == "") None
    else Some(input.trim.split("\\s+"))
}

//########################### 5.7  变量声明中的模式 #################################
/**
  * 在变量声明中使用模式
  */
val (x1, y1) = (1, 2) //元组也有其对应的unapply方法,跟上面的逻辑有差别,但是机理都一样
BigInt(10) /% 3 //返回值是多个或多种类型,返回的是元组,这也是元组存在的意义
val (q, r) = BigInt(10) /% 3
val arr2 = Array(1, 7, 2, 9)
val Array(first, second, _*) = arr2
//########################### 5.8  For表达式中的模式 #################################
/**
  * 在for表达式中的使用提取器
  * for会忽略失败的匹配
  */

import scala.collection.JavaConverters._

for ((k, v) <- System.getProperties.asScala)
  println(k + " -> " + v)

//匹配k=""的map
for ((k, "") <- System.getProperties.asScala)
  println(k)

//for循环中使用if守护,与上面提取器效果等价
for ((k, v) <- System.getProperties.asScala if v == "")
  println(k)


//########################### 5.9  样例类 #################################
/**
  * 样例类首先是类,除此之外,它是为模式匹配而优化的类,样例类用case关键字进行生命
  * 当你生命样例类时,如下几件事自动发生
  * 1.构造器中的每个参数都成为val,除非显示生命为var
  * 2.在伴生对象中提供apply方法让你不用new关键字就能构造相应的对象,如Currency(28.8,"EUR")
  * 3.提供unapply方法让模式匹配可以工作(没有该方法的类就不能支持模式匹配)
  * 4.将生成toString、equest、hasCode和copy方法,也可显示写出进行扩展
  * 除上述外,样例类和其他类完全一样,你可以添加方法和字段,扩展他们
  */

abstract class Amount

//如果不加case,Dollar就会没有apply构造方法和unapply模式匹配方法,Dollar(1000.0)和case Dollar(v) => "$" + v都会编译报错
//如果要实现模式匹配不写case也行,自己手动写apply和unapply方法
case class Dollar(value: Double) extends Amount

case class Currency(value: Double, unit: String) extends Amount

case object Nothing extends Amount

for (amt <- Array(Dollar(1000.0), Currency(1000.0, "EUR"), Nothing)) {
  val result = amt match {
    case Dollar(v) => "$" + v
    case Currency(_, u) => "Oh noes,I got " + u
    case Nothing => ""
  }
  println(amt + ": " + result)
}

//########################### 5.10  Copy方法和带名参数 #################################
/**
  * copy创建一个与现有对象值相同的新对象,并可以通过带名参数来修改某些属性
  */
val amt = Currency(29.95, "EUR")
amt.copy(value = 19)
amt.copy(unit = "CHF")


//########################### 5.11  Case语句的中置表达式 #################################
List(1, 7, 2, 9) match {
  case first :: second :: rest => println(first); println(second); println(rest)
  case _ => 0
}
List(List(1, 7), List(2, 9)) match {
  case (first :: second) :: rest => println(first); println(second); println(rest)
  case _ => 0
}


//########################### 5.12  匹配嵌套结构 #################################

/**
  * 样例类经常被用于嵌套结构.例如,某个商店售卖的物品,有时会将多个物品一起打着出售,我们有以下抽象:
  */

abstract class Item

case class Article(description: String, price: Double) extends Item

case class Bundle(description: String, discount: Double, items: Item*) extends Item


val special = Bundle("Father's day special", 20.0,
  Article("Scala for the Impatient", 39.95),
  Bundle("Anchor Distillery Sampler", 10.0,
    Article("Old Potrero Straight Rye whiskey", 79.95),
    Article("Junipero Gin", 32.95)))

//79.95+32.95-10.0 + 39.95-20=122.85

//绑定第一个Article的描述
special match {
  case Bundle(_, _, Article(descr, _), _*) => descr
}
//可以通过@表示法将嵌套的值绑定到变量.  _*绑定剩余Item到rest

special match {
  case Bundle(_, _, art@Article(_, _), rest@_*) => (art, rest)
}

//与上面rest与rests@_*的区别,rest@_*使用可变集合Seq(WrappedArray)包装起来的一个Bundle对象

special match {
  case Bundle(_, _, art@Article(_, _), rest) => (art, rest)
}


//计算某个Item价格的函数
def price(it: Item): Double = it match {
  case Article(_, price) => price
  case Bundle(_, discount, items@_*) => items.map(price _).sum - discount
  //  case Bundle(_, discount, items@_*) => items.map(price(_)).sum - discount
}

price(special)

//########################### 5.13  密封类 #################################
/**
  * 如果想让case类的所有子类都必须在申明该类中定义,可以将样例类的通用超类生命为sealed,叫做密封类
  * 密封就是外部用户不能在其他文件中定义子类
  */

//########################### 5.14  模拟枚举 #################################
/**
  * 样例类可以模拟出枚举类型
  */
sealed abstract class TrafficLightColor

case object Red extends TrafficLightColor

case object Yellow extends TrafficLightColor

case object Green extends TrafficLightColor

for (color <- Array(Red, Yellow, Green))
  println(
    color match {
      case Red => "stop"
      case Yellow => "hurry up"
      case Green => "go"
    }
  )

//########################### 5.15  偏函数 #################################
/**
  * 这个例子从反面展示了:通过case语句组合去是实现一个偏函数是多么简洁
  * PartialFunction[A,B] A:入参类型 B:返参类型
  * isDefinedAt用来告知调用方这个偏函数接受参数的范围,通常在调用偏函数前调用检测
  * 如果参数A类型不符,会报运行时异常scala.MatchError
  * 偏函数详解参考博客:  https://blog.csdn.net/bluishglc/article/details/50995939
  */
val f: PartialFunction[Char, String] = {
  case '+' => "+"
  case '-' => "-"
}
f('-')
f.isDefinedAt('0')
f('0')

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值