快学Scala笔记(五)

来源:互联网 发布:上海大学图书馆数据库 编辑:程序博客网 时间:2024/06/06 02:39

十四、模式匹配和样例类

var sign = ...var ch: Char = ...ch match {   case '+' => sign = 1   case '-' => sign = -1   case _ => sign = 0     // 否则代码会抛出MatchError异常}

守卫

ch match {   case '+' => sign = 1   case '-' => sign = -1   case _ if Charactor.isDigit(cn) => digit = Charactor.digit(ch, 10)    case _ => sign = 0}

守卫可以是任何Boolean条件

模式中的变量

case 关键字后接一个变量,那么匹配的表达式会赋值给这个变量

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

变量模式可能会与常量表达式发生冲突

import scala.math._x match {    case Pi => ...    // 变量必须以小写字母开头    case `pathSeperator` => ...   // 小写字母开头的常量}

类型模式

可以匹配表达式的类型

obj match {    case x: Int => x    case s: String => Integer.parseInt(s)    case _: BigInt => Int.MaxValue    case _ => 0}

在类型匹配的时候必须给一个变量名,否则会拿对象本身来匹配
匹配发生在运行期,Java虚拟机中泛型的类型信息是被擦掉的,因此不能用类型来匹配特定的Map类型

case m : Map[String, Int] => ...    // 错误!case m : Map[_, _] => ...           // OK

匹配数组、列表和元组

attr match {    case Array(0) => "0"   // 匹配包含0的数组   case Array(x, y) => x + " " + y   // 匹配任何带有两个元素的数组   case Array(0, _*) => "0 ..." // 匹配任何以0开始的数组   caes _ => "somthing else"}

匹配List

lst match {   case 0 :: Nil => "0"   case x :: y :: Nil => x + " " + y   case 0 :: tail => "0 ..."   caes _ => "something else"}

匹配元组

pair match {   case (0,_) => "0 ..."   case (y,0) => y + " 0"   case _ => "neither is 0"}

提取器

提取器带有从对象中提取值的unapply或unapplySeq方法的对象
正则表达式是另一个适合使用提取器的场景,如果正则表达式有分组,可以用提取器来匹配每个分组

var pattern = "([0-9]+ ([a-z]+)".r"99 bottles" match {    case pattern( num, item ) => ...}

变量声明中的模式

val ( x, y ) = (1,2)var (q,r) = BigInt(10) /% 3var Array(first, second, _* ) = arr

for表达式中的模式

for( (k,v) <- System.getProperties() )   ...for( (k,v) <- System.getProperties() if v == "" )   ...

样例类

用于模式匹配

abstract class Amount// 扩展自常规类的样例类case class Dollar(value:Double) extends Amountcase class Currency(value:Double, until: String) extends Amount// 针对单例的样例对象case object Nothing extends Amount
// 样例类匹配Amount对象amt match {    case Dollar(v) => "$" + v    case Currency( _, u ) => u + "$"    case Nothing => ""}

copy方法和带名参数

样例类的copy方法创建一个与现有对象值相同的新对象,可修改某些属性

var amt = Currency( 29.95, "EUR")val price = amt.copy(value=19.95, unit="CHF")   //

case语句中的中置表示法

如果unapply方法产生一个对偶,则可以在case语句中使用中置表示法,该特性可以用来匹配序列
例如,每个List对象要么是Nil,要么是样例类 ::

case class ::[E]( head: E, tail: List[E] ) extends List[E]

可以写成:

lst match { case h :: t => ... }   // 等同于 case ::(h,t),将调用 ::.unapply(lst)

将解析结果组合在一起的 ~ 样例类

result match { case p ~ q => ... }result match { case p ~ q ~ r => ... }

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

case first :: second :: rest   //  case ::(first, ::(second, rest) )

匹配嵌套结构

abstract class Itemcase class Article( description: String, price: Double ) extends Itemcase class Bubble( description: String, discount: Double, item: Item*) extends ItemBundle("Father's day", 20.0, Article("Scala",39.95) )Bundle("Father's day", 20.0, Article("Scala",39.95), Article("Scala",39.95) )

模式可以匹配到特定的嵌套

case Bundle( _, _, Article(descr,_), _* ) => ...    // descr 是第一个 Article 的描述case Bundle( _, _, art @ Article( _, _ ), rest @ _* ) => ... // art 是第一个article, rest是剩余Item的序列case Bundle( _, _, art @ Article( _, _ ), rest => ...   // 一个Articel加上一个Item

示例

def price( it: Item ) : Double = it match {   case Article( _, p ) => p   case Bundle( _, disc, its @ _* ) => its.map( price _ ).sum - disc}

密封类

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

sealed abstract class Amount case class Dollar( value: Double ) extends Amountcase class Currency( value: Double, unit: String ) extends Amount

密封类的所有子类都必须在与该密封类相同的文件中定义
如果某个类是密封的,那么在编译期所有子类就是已知的,因而编译器可以检查模式语句的完整性。

模拟枚举

样例类可以让你在Scala中模拟出枚举类型
如果觉得这样的方式有些过重,可以使用之前介绍的Enumeration助手类

sealed abstract class TrafficLightColorcase object Red extends TrafficLightColorcase object Yellow extends TrafficLightColorcase object Green extends TrafficLightColorcolor match {    case Red => "stop"    case Yellow => "hurry up"    case Green => "go"}

Option类型

Option表示可能存在也可能不存在的值, 如样例子类Some包装了某个值Some(“Fred”),而样例对象None表示没有值,这比使用空字符串的意图更加清晰,比使用null来表示缺少某值的做法更加安全。
Option支持泛型,Some(“Fred”) 的类型为 Option[String]
Map类的get方法返回一个Option

scores.get("Alice") match {    case Some(score) => println( score )    case None => println( "No score" )}// 等价于:println( scores.getOrElse( "Alice", "No score" ) ) )

如果想略过None值,可以用for推导式

for( score <- scores.get("Alice") ) println( score )scores.get("Alice").foreach( println _ )

如果get方法返回None,什么都不会发生,如果返回Some,则score将被绑定到它的内容

偏函数

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

var f : PartialFunction[Char, Int] = { case '+' => 1; case '-' => -1 }f( '-' )   // 调用 f.apply('-')  返回 -1f.isDefinedAt('0')    // falsef('0')   // 抛出 MatchError

有一些方法接受PartialFunction作为参数,举例来说,GenTraversable特质的collect方法将一个偏函数应用到所有在该偏函数有定义的元素,并返回包含这些结果的序列

"-3+4".collecct { case '+' => 1; case '-' => -1 }    // Vector( -1, 1 )