5.Scala-匹配模式

第5章 模式匹配

5.1 switch

与 default 等效的是捕获所有的 case_ 模式。如果没有模式匹配,抛出
MatchError,每个 case 中,不用 break 语句。

和 if 一样,match 也会返回值:
你可以在 match 中使用任何类型,而不仅仅是数字。 
 
 
笔记:
def match1() = {
  var result = 0
  val op : Char = '-'

  op match {
    case '+' => result = 1
    case '-' => result = -1
    case _ => result = 0
  }

  println(result) //-1
}

match1()

5.2 守卫

像 if 表达式一样,match 也提供守卫功能,守卫可以是任何 Boolean 条件:

笔记:

def match2() = {
  for(c <- "+-*/123"){
    c match {
      case _ if Character.isDigit(c) => println("这是一个数字" + c)
      case '+' => println("字符为+号")
      case '-' => println("字符为-号")
      case '*' => println("字符为*号")
      case '/' => println("字符为/号")
      case _ => println("通配")
    }
  }
}

match2()

5.3 模式中的变量

如果 case 关键字后面跟着一个变量名,那么匹配的表达式会被赋值给那个变量。 
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)
}
Scala 中变量必须以小写字母开头,常量用大写字母,如果常量用小写字
母开头需要加反引号。 

5.4 类型模式

 可以匹配对象的任意类型,但是不直接匹配泛型类型,这样描述比较抽象,看下面的例子:

这样做的意义在于,避免了使用 isInstanceOf 和 asInstanceOf 方法。

 

 笔记:

//类型模式
def match3() = {
  val a = 6

  val obj = if(a == 1) 1
  else if(a == 2) "2"
  else if(a == 3) BigInt(3)
  else if(a == 4) Map("aa" -> 1)
  else if(a == 5) Map(1 -> "aa")
  else if(a == 6) Array(1, 2, 3)
  else if(a == 8) Array("aa")
  else if(a == 7) Array("aa", 1)

  val r1 = obj match {
    case i : Int => i
    case s : String => s
    case bi : BigInt => bi
    case m1 : Map[String, Int] => println("Map[String, Int]")
    case m2 : Map[Int, String] => m2
    case a1 : Array[Int] => a1 
    case a3 : Array[String] => a3
    case a2 : Array[any] => a2
  }

  println(r1 + ":" + r1.getClass.getName)

}

match3()

5.5 匹配数组、列表、元组

Array(0) 匹配只有一个元素且为 0 的数组。
Array(x,y) 匹配数组有两个元素,并将两个元素赋值为 x 和 y。
Array(0,_*) 匹配数组以 0 开始。
1)匹配数组:
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)
}

2)匹配列表

同样的方式可以应用于列表

 3)匹配元组

集合元素通过匹配绑定到变量,这样的操作叫做“析构”。

笔记:

//匹配数组
def match4() = {

  for(arr <- Array(Array(0), Array(1, 0), Array(0, 1, 1), Array(1, 1, 0), Array(1, 1, 1, 0))){
    arr match {
      case Array(0) => println("Array(0)")
      case Array(x, y) => println("Array(x, y)" + ":" + x + "," + y)
      case Array(x, y, z) => println("Array(x, y, z)" + ":" + x + "," + y + "," + z)
      case Array(1, arr @ _*) => println("Array(1, arr @ _*)" + ":" + arr.length)
      case _ => println("通配")
    }
  }
}

match4()

5.6 提取器

模式匹配,什么才算是匹配呢?即,case 中 unapply 方法返回 some 集合则为匹配成功,

返回 none 集合则为匹配失败。下面看几个例子:

1)unapply

-- 调用 unapply,传入 unmber

-- 接收返回值并判断返回值是None,还是 Some

-- 如果是 Some,则将其解开,并将其中的值赋值给 n(就是 case Square(n) 中的 n)

创建 object Square:
object Square{
    def unapply(z: Double): Option[Double] = Some(math.sqrt(z))
}

模式匹配使用:
val number: Double = 36.0

number match {
    case Square(n) => println(s"square root of $number is $n")
    case _ => prinpln("nothing matched")
}

 

  到底什么时候用 unapply 什么时候用 unapplySeq,要看参数个数。
注意:如果要提取单个值,则应该返回一个目标类型的 Option,例如
Option[Int],而不是 Option[(Int)];无参数的提取器可以用于 boolean 检查。

 

5.7 变量声明中的模式

 match 中每一个 case 都可以单独提取出来,意思是一样的,如下:

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

5.8 for表达式中的模式

在 for 表达式中使用提取器: 

import scala.collection.JavaConverters._

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

 笔记:

//for 循环中的模式匹配
import scala.collection.JavaConverters._

def match6() = {
  
  for ((k, v) <- System.getProperties.asScala)
    println(k + " -> " + v)
  for ((k, "") <- System.getProperties.asScala)
    println(k)
  for ((k, v) <- System.getProperties.asScala if v == "")
    println(k)
  
  
}
match6()

5.9 样例类

样例类首先是类,除此之外它是为模式匹配而优化的类,样例类用 case关键字进行声明: 

1)样例类的创建

abstract class Amount
case class Dollar(value: Double) extends Amount
case class Currency(value: Double, unit: String) extends Amount
case object Nothing extends Amount

2)当我们有一个类型为 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 => "" 
}
    // Note that amt is printed nicely, thanks to the generated toString
    println(amt + ": " + result)
}

笔记:

//样例类
def match7() = {
  for (e <- Array(Dollar(1000.0), Currency(1000, "EUR"))) {
    e match {
      case Dollar(v) => println(v)
      case Currency(k, v) => println(k + "," + v)
    }
  }
}

match7()

5.10 Copy方法和带名参数

copy 创建一个与现有对象值相同的新对象,并可以通过带名参数来修改某些属性。 
val amt = Currency(29.95, "EUR")
val price = amt.copy(value = 19.95)
println(amt) //Currency(29.95,EUR) println(price)  //Currency(19.95,EUR) println(amt.copy(unit
= "CHF")) //Currency(29.95,CHF)

5.11 Case语句的中置(缀)表达式

什么是中置表达式?1 + 2,这就是一个中置表达式。如果 unapply 方法产出一个元组,你可以在 case 语句中使用中置表示
法。比如可以匹配一个 List 序列,可以如下表示: 
List(1, 7, 2, 9) match { 
  case first :: second :: rest => first + second + rest.length 
  case _ => 0
}

笔记:

//Case语句的中置(缀)表达式
def match8() = {
  val list = List(1, 2, 3, 4, 5, 6)
  list match {
    case l1 :: l2 :: l3 => println(l1 + "," + l2 + "," + l3)
  }
}

match8() //1,2,List(3, 4, 5, 6)

5.12 匹配嵌套结构

样例类经常被用于嵌套结构。例如,某个商店售卖的物品,有时,会将
多个物品一起打着出售,我们有以下抽象:
 
1)创建样例类
abstract class Item
case class Article(description: String, price: Double) extends Item
case class Bundle(description: String, discount: Double, items: Item*) extends Item

2)匹配嵌套结构

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("Junípero Gin", 32.95)))

3)将 descr 绑定到第一个 Article 的描述

val result1 = special match {
  case Bundle(_, _, Article(descr, _), _*) => descr
}

println(result1)

4)通过@表示法将嵌套的值绑定到变量。_*绑定剩余 Item 到 rest

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

5)不使用 _* 绑定剩余 Item 到 rest

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

println(result3)

 6)计算某个 Item 价格的函数,并调用

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

笔记:

//嵌套类的匹配
def match9() = {
  val sale = Bundle("秘籍", 10,
    Article("九阳神功", 40),
    Bundle("系列",20,
      Article("Java系列", 80),
      Article("小说系列", 30)))

  val result1 = sale match {
    case Bundle(_, _, Article(descr, _), temp @ _*) => descr + "," + temp
  }

  println(result1) //九阳神功,WrappedArray(Bundle(系列,20.0,WrappedArray(Article(Java系列,80.0), Article(小说系列,30.0))))

  val result2 = sale match {
    case Bundle(_, _, art @ Article(_, _), rest @ _*) => (art, rest)
  }
  println(result2) //(Article(九阳神功,40.0),WrappedArray(Bundle(系列,20.0,WrappedArray(Article(Java系列,80.0), Article(小说系列,30.0)))))

  val result3 = sale match {
    case Bundle(_, _, art @ Article(_, _), rest) => (art, rest)
  }
  println(result3) //(Article(九阳神功,40.0),Bundle(系列,20.0,WrappedArray(Article(Java系列,80.0), Article(小说系列,30.0))))


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

  println(price(sale))

}


match9()

5.13 密封类

如果想让 case 类的所有子类都必须在申明该类的相同的文件中定义,可
以将样例类的通用超类声明为 sealed,叫做密封类,密封就是外部用户不能在
其他文件中定义子类。
sealed abstract class TrafficLightColor
  case object Red extends TrafficLightColor
  case object Yellow extends TrafficLightColor
  case object Green extends TrafficLightColor

5.14 模拟枚举 

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

笔记:

//模拟枚举
def match10() : Unit = {

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

5.15 偏函数 

偏函数,它只对会作用于指定类型的参数或指定范围值的参数实施计算

val f: PartialFunction[Char, Int] = {
  case '+' => 1
  case '-' => -1
}

println(f('-')) //-1
println(f('+')) //1
println(f.isDefinedAt('0')) //false

map 和 collect:

    //额外讨论
    val list = List(1, 2, 3, 4, 5)
    val result1 = list.map(_ * 2)
    println(result1 + ", result1") //List(2, 4, 6, 8, 10)

    val result2 = list.collect{case x => x * 2}
    println(result2 + ", result2") //List(2, 4, 6, 8, 10)

//    val result3 = List(1, 2, 3, 4, 5, "haha").map{case i: Int => i * 2}
//    println(result3 + ", result3") //scala.MatchError

    val result4 = List(1, 2, 3, 4, 5, "haha").collect{case i: Int => i * 2}
    println(result4 + ", result2") //List(2, 4, 6, 8, 10)

再深入探讨一点点:

我们定义一个将 List 集合里面数据 +1 的偏函数

val f1 = new PartialFunction[Any, Int] {
  def apply(any: Any) = any.asInstanceOf[Int] + 1

  def isDefinedAt(any: Any) = if (any.isInstanceOf[Int]) true else false
}
val rf1 = List(1, 3, 5, "seven") collect f1 //collect 会调用 isDefinedAt 函数再执行 apply 方法,而 map 会直接执行 apply
println(rf1) //List(2, 4, 6)

  

如上功能,等同于:

def f2: PartialFunction[Any, Int] = {
  case i : Int => i + 1
}
val rf2 = List(1, 3, 5, "seven") collect(f2)
println(rf2) //List(2, 4, 6)
原文地址:https://www.cnblogs.com/LXL616/p/11117143.html