//########################### 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')