《快学scala》学习笔记--第14章 模式匹配和样例类

本篇学习笔记是第二遍看《快学scala》所写,对scala语言有一定的基础

14.1 更好的switch

    val ch: Char = '*'
    ch match {
      case '+' => println("加法")
      case '*' => println("乘法")
      case _ => println("木有")
    }

match是一个表达式,而不是语句,会有返回值

    val ch: Char = '*'
    val x:Int = ch match {
      case '+' => 0
      case '*' => 1
      case _ => 2
    }
    println(x)

14.2 守卫

守卫可以是任何布尔条件,在下面的例子中,只有在守卫模式不能匹配的情况下才执行所有模式匹配

    val ch:Char = 3
    var sign:Int = 0
    ch match {
      case '+' => println("++++")
      case '*' => println("====")
      case _ if ch >= 0 && ch <= 9 => sign = ch
      case _ => println("---")
    }
    println(sign)

14.3 模式中的变量

case _是这种特性的特殊情况,变量名是_

    val c: Char = '+'
    val ch:Char = 9
    var sign:Int = 0
    ch match {
      case '+' => println("++++")
      case '*' => println("====")
      case c if c >= 0 && c <= 9 => sign = c
      case _ => println("---")
    }
    println(sign)
  • 变量必须以小写字母开头
  • 如果有一个小写字母开头的常量,则需要将它包在反引号中。

14.4 类型模式

匹配列表中的每一个元素的类型,将每一个元素映射成=>后面的string类型

    val ls = List(1,3,4,"one","two",4.5)
    val l = ls.map(_ match {
      case x:Int => "int x:" + x
      case s:String => "string s:" + s
      case _ => "other"
    })
    l.foreach(println)

14.5 匹配组、列表和元组

要匹配数组的内容,可以在模式中使用Array表达式,_*符号匹配数组中余下的内容

    val arr1 = Array(Array(0),Array(3,4),Array(0,4,6),Array("fad","fff"))
    arr1.map(_ match {
      case Array(0) => println(0)
      case Array(x,y) => println(x,y)
      case Array(0,_*) => println("...")
      case _ => println("something")
    })

匹配列表中的元素

    val ls = List(List(0),List(3,4),List(0,4,5),List("fa","aaa","tttt"))
    ls.map(_ match {
      case 0::Nil => println(0)
      case x :: y :: Nil => println(x,y)
      case 0 :: tail => println(tail.foreach(println))
      case x :: tail => println("====")
      case _ => println("something")
    })

匹配元组

    val ls = List((0,4),("fa",0),(3,4))
    ls.map(_ match {
      case (0,_) => println(0)
      case (x,y) => println(x,y)
      case _ => println("something")
    })

14.6 提取器

前一节中匹配数组、列表和元素的功能是依靠提取器实现的
提取器机制:带有从对象中提取值得unapplyunapplySeq方法得对象。unapply提取固定数量得对象,而unapplySeq提取得是一个序列
Array.unapplySeq(arr)产出一个序列的值。

14.8 for表达式中的模式

    val ls = List((1,2),(3,4),(4,5))
    for ((k,v) <- ls if k % 2 == 1)
      println(k,v)

14.9 样例类

可以用模式匹配来匹配样例类,并将属性值绑定到变量

    case class Dollar(value:Double)
    case class Currency(value:Double,unit:String)
    case object Yuan
    val ls = List(Dollar(2.3),Currency(4.5,"10"),Yuan)
    ls.map(_ match {
      case Dollar(v) => println(s"Dollar:$v")
      case Currency(_,u) => println(s"got $u")
      case Yuan => println("lalala")
      case _ => println("----")
    })

*样例类实例使用(),样例对象不适用圆括号

==声明样例类的时候有如下几件事自动发生:==
- 构造器中的每一个参数都成为val,除非被显式地声明为var
- 在伴生对象中提供apply方法,可以不用new关键字就能构造出相应的对象,比如Dollar(2.3)
- 提供unapply方法让模式匹配可以工作
- 将生成toString equals copy方法,除非显式给出定义

14.10 copy方法和带名参数

copy方法创建一个与现有对象值相同的新对象,可以用带名参数修改某些属性

    val amt = Currency(34.2,"RUR")
    val price = amt.copy()
    val price1 = amt.copy(value = 13)
    val price2 = amt.copy(unit = "CHF")

14.11 case语句中的中置表示法

如果unapply方法产出一个对偶,可以在case语句中使用中置表示法,19章将会看到将解析结果组合在一起的`~样例类:

result match {case p ~ q => ...} //等同于case ~(p,q)

*如果操作符以冒号结尾,则他是从右向左结合的

中置表示法可以用于任何返回对偶的unapply方法:

    case object +: {
      def unapply[T](input: List[T]):Option[(T,List[T])] =
        if(input.isEmpty) None else Some((input.head,input.tail))
    }
    1+:7+:2+:Nil match {
      case first +: second +: rest => println(first + second + rest.length)
    }

14.12 匹配嵌套结构

三个样例类继承Item抽象类,Bundle和Boolean类嵌套Article和Bundle样例类,
可以用@表示法将嵌套的值绑定到变量

    abstract class Item
    case class Article(desc:String,price:Double) extends Item//继承同一个抽象类
    case class Bundle(desc:String,discount:Double,item:Item) extends Item
    val peak = Bundle("Father's Day is special",20.0,Article("scala is nice",45.5))
    case class Boolean(desc:String,dis:Double,item:Item*)//Item抽象类列表
    val amt = Boolean("great",23.3,Bundle("nice",45.4,Article("wonderful",45.4)),Article("scala is nice",45.5))
    val ls = List(peak,amt)
    ls.map(_ match {
      case Bundle(_,_,Article(desc,price)) => println(s"bundle ${desc}:${price}")
      case Boolean(_,_,art @ Bundle(_,_,_),rest @ _*) => println(s"boolean ${art.desc}:${art.discount},${rest.head}")
      case Boolean(_,_,art @ Bundle(_,_,_),rest) => println(s"boolean ${art.desc}")//匹配继承Item抽象类的元素只有两个
      case _ => println("None")
    })

14.14 密封类

当用样例类来做模式匹配的时候,想让编译器帮助确保已经列出了所有可能的选择,要达到这个目的,需要将样例类的通用超类声明为sealed

  • 密封类的所有子类都必须在与该密封类相同的文件中定义。
  • 如果某个类是密封的,那么在编译期所有子类就是可知的,因此==编译器可以检查模式语句的完整性==,让同一组样例类都扩展自某个密封的类或者特质是一个好的做法
    sealed abstract class Amount
    case class Red(bottom:Double) extends Amount
    case object Green extends Amount
    case object Gray extends Amount
    val color:Amount = Red(3.4)//需要显式声明color的类型,不然就会变成Red
    color match {
      case Red(_) => println("red")
      case Green =>println("green")
      case Gray => println("fad")
    }

14.16 Option类型

标准类库的Option类型用样例类来表示那种可能存在,也可能不存在的值。这比空字符串更加清晰,比null做法更加安全。
* map类的get方法返回一个Option。如果对于给定的键没有对应的值,则get返回None,如果有值,该值包在Some中返回
* 请以不要直接使用get方法,返回None的情况可能会使程序死掉,尽量用getOrElse

Option[+A]密封抽象类,Some和None继承Option类

14.17 偏函数

被包括在花括号内的一组case语句是一个偏函数——==并非对所有输入值都有定义的函数==。它是PartialFunction[A,B]类的一个实例,该类有两个方法:apply方法从匹配到的模式计算函数值,而isDefinedAt方法在输入至少匹配其中一个模式时返回true

    val f:PartialFunction[Char,Int] = {
      case '+' => 1
      case '-' => -1
    }
    println(f('+'))//1
    //println(f('0'))//error
    println(f.isDefinedAt('0'))//false

猜你喜欢

转载自blog.csdn.net/weixin_36926779/article/details/81434735