模式匹配

模式匹配

Scala 中的模式匹配类似于java中的 switch 语法, 但是更加强大

模式匹配语法中, 使用 match关键字声明, 每个分支采用 case 关键字进行声明, 当需要匹配是,会在第一个case 分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断.

如果所有 case 都不匹配, 那么会执行 case _ 分支, 类似于 java 中的 default语句

1.1基本应用

ex1: 输入1,2,3 会打印对应的颜色

object Color {

    def main(args: Array[String]): Unit = {
        //从键盘获取数字
        val i: Int = StdIn.readInt()
        i match {
            case 1 => println("red")
            case 2=> println("green")
            case 3=> println("yellow")
            case _=> println("您的输入有误")
        }
    }

}
说明:
  • 如果所有case都不匹配,那么会执行case _ 分支,类似于 Java 中default语句
  • 如果所有case都不匹配,又没有写case _ 分支,那么会抛出异常
  • 每个case中,不用break语句,自动中断case
  • 可以在match中使用其它类型(任意类型),而不仅仅是字符
  • => 等价于 java swtich:
  • =>后面的代码块到下一个 case, 是作为一个整体执行,可以使用{} 扩起来,也可以不括

ex2 : 加减乘除

object MatchDemo2 {
  def main(args: Array[String]): Unit = {
    var a1 = 10
    var a2 = 20

    var operChar = "*"
    val res = operChar match {
      case "+" => a1 + a2
      case "-" => a1 - a2
      case "*" => a1 * a2
      case "/" => a1 / a2
      case _ => "你的运算符负不正确"
    }
    println("res = " + res)
  }
}

1.2 守卫

想要表达某个范围的数据, 就需要在匹配模式匹配中增加条件"守卫"

object MatchDemo3 {
  def main(args: Array[String]): Unit = {
    for (ch <- "+-3&%") {
      var digit = 0
      val sign = ch match {
        case '+' => 1
        case '-' => -1
        case _ if Character.isDigit(ch) => digit = Character.digit(ch, 10)
        case _ => 0
      }
      println("ch = " + ch)
      println("sign = " + sign)
      println("digit = " + digit)
      println("---------")
    }
  }
}

1.3 变量和常量

如果在case关键字后跟变量名,那么 match 前表达式的值会赋值给那个变量

object MatchVar {

    def main(args: Array[String]): Unit = {
        var ch = 0

        'z' match {
            case 'a' => println("a...")
            case 'b' => println("b...")
            case 'c' => println("c...")
            case ch => println("ch=" + ch)
        }
    }
}

注意

  • 如果 case ch 匹配成功了, 后面即使跟上case _ 也没有用
  • 可以把case _ 看成 case 变量名 的特例 , 把_看成一个变量来对待

但是:

按照约定, Scala 期望模式变量名都以小写字母开头, 而常量名则是大写字母(只要首字母大写也可以)。

如果你使用大写字母的名称,Scala 将会在作用域范围内查找常量。

但是,如果使用的是非大写字母的名称,它将只假设其是一个模式变量—-在作用域范围内任何具有相同非大写字母的名称都将会被忽略。

在下面的代码中,我们定义了一个和字段具有相同名称的模式变量, 但是这段代码将不会给出我们想要的结果—–模式变量隐藏了(Sample 类的)max 字段。

object PatternTest {
    def main(args: Array[String]): Unit = {
        val sample = new Sample
        sample.process(1000)
    }
}

class Sample {
    val max = 10000
    def process(input: Int): Unit = {

        input match {
            case max => println(s"You matched max")
        }
    }
}
说明:
  • 你会发现 scala 把 max推断为模式变量, 而不是模式常量.

  • 解决办法:

    • 办法1: 明确指定作用域:this.max

    • 办法2: 使用反引号类给 scala 提示.

       case `max` => .....
      
    • 办法3: 把max变成大写: MAX

1.4 模式匹配

object MatchType {

    def main(args: Array[String]): Unit = {

        val list = List(1, 3.2, "sa",true)

        var result = list match {
            case a: List[Int] => println("...Int类型...")
            case b : List[String] => println("...string...")
            case c :List[Double] =>println("...double...")
            case d:List[Boolean] => println("...boolean...")

        }
        println(result)
    }
}
说明:
  • 在进行类型匹配时,编译器会预先检测是否有可能的匹配,如果没有则报错
  • 如果类型匹配成功之后会把s的值赋值给case后面跟着的变量, 而不需要做类型的转换.
  • 对数组来说提供数组元素中的类型是必要的,因为数组的底层类型确实是确定的.比如:Array[Int]这里的Int是必须的
  • 但是对集合类型比如 Map, 提供泛型类型是无用的. 因为 Java 的"泛型擦除". Map[Int, String]和Map[Int, Int] 在匹配的时候没有区别. 所以应该使用通用的泛型:Map[_, _]
import scala.io.StdIn

object MatchType {
  def main(args: Array[String]): Unit = {
    var s: Any = Map("a" -> 11, "b" -> 22)

    var result = s match {
      case d: Map[_, _] => "匹配到的是Map"
      case _ => "啥都没有匹配到"

    }
    println(result)
  }
}

1.5 匹配数组

object PatternDemo5 {
    def main(args: Array[String]): Unit = {
        // Array(1,2,3) == Array(1,2,3)
        var arr1 = Array(1, 2, 3, 5, 6)
        
        arr1 match {
            //匹配三个元素,只能是三个元素,且为123
            //            case Array(1, 2, 3) => println("Array(1,2,3)")
            
            //匹配四个元素,且前两个元素为1,2
            //            case Array(1, 2, _, _) => println("Array(1,2,_,_)")
            
            //匹配四个元素,第三个元素用变量接收
            //            case Array(1, 2, a, _) => println("Array(1,2,_,_)  " + a)
            
            //匹配前两个元素为1,2,后面是什么都行
            //            case Array(1, 2, _*) => println("Array(1,2, _*)  ")
            
            //匹配前两个为1,2,  后面为一个数组
            // 在未来 @ 会变成 :
            //            case Array(1, 2, rest@_*) => println("Array(1,2, rest@_*)  " + rest.mkString(", "))
            case Array(_, _, rest@_*) => println("Array(_,_, rest@_*)  " + rest.mkString(", "))
            case _ => println("no ")
            
        }
        
    }
}

1.6 匹配列表

def main(args: Array[String]): Unit = {
    val arr: List[Int] = List(1, 2, 3, 5, 6)
    val res = arr match {
      //case List(1, 2) => "List(1, 2)"
      //case List(1, _*) => "匹配以 1 开头的列表"
      //case 1 :: 2 :: 3 :: Nil => "匹配List(1,2,3)"
      case 1 :: abc => println(abc); "匹配以 1 开头的列表"
      case _ => "啥也可没有匹配到"
    }
    println(res)
  }

1.7 匹配元组

object MatchTouple {

    def main(args: Array[String]): Unit = {

//        val tup1:(Int , String)= (1,"niefeng")
        val tup2:(Int , String , String)= (2,"bujingyun","chuchu")

        tup2 match {
            case (a,b,c) => println(s"a=$a,b=$b,c=$c")
        }
    }
}

1.8 对象匹配(提取器)

对象匹配,什么才算是匹配呢?,规则如下:

  1. case中对象的unapply方法(提取器)返回some集合则为匹配成功
  2. 返回None集合则为匹配失败

备注:

  1. SomeNone 都是Option的子类型
  2. 2.11.1 开始, scala 为了性能上的优势, 放松了对unapply返回的值的类型的限制(不必必须是 Option). 返回的类型只要具有以下方法就可以:
    • def isEmpty: Boolean
      返回值: true用来标记匹配成功, false匹配失败
    • def get: T 返回值: 表示提取到的值.
  3. 其实SomeNone 均包含上面的两个方法.
  4. 另外一种情况: 如果unapply返回一个Boolean值, 则只表示匹配成功或者失败. 如果是 true则表示匹配成功, 但是不会提取到任意的值.
  5. 不过, 大部分情况下, 我们还是把要提取的数据封装到Some中返回.
1. 提取单个对象
object MatchObj {

    def main(args: Array[String]): Unit = {
        val num = 100.0

        num match {
            case Square(z) => println("z =" +z)
        }
    }
}
object Square{
    def apply(a:Double): Double = a * a

    def unapply(arg: Double): Option[Double] = Some(math.sqrt(arg))
}


result: 10.0

执行流程说明:

  1. case匹配的过程中, 如果碰到这种(伴生对象(参数))的形式的时, 就会调用这个伴生对象的unapply方法, 来提前对象.
  2. 调用unapply的时候, 会把前面的匹配表达式(本例中的num)传递给unapply
  3. 如果unapply返回的是Some, 则表示匹配成功. 并unapply的返回值赋值给伴生对象(参数)中的参数(本例中的z), z其实就是提取到的对象
  4. 如果unapply返回的None, 则表示匹配失败.
  5. 继续下一个case的匹配…
2. 提取多个对象
object MatchObjs {

    def main(args: Array[String]): Unit = {
        val s = "yangguo,xiaolongnv"
        val res = s match {
            case Names(one,two) => println(s"成功! one =$one,two =$two")
            case _ => println("匹配失败")

        }
    }
}

object  Names{
    def unapplySeq(str:String)={
        if (str.contains(","))
            Some(str.split(","))
        else
            None
    }
}

在这里插入图片描述

执行过程说明:

  1. case Names(one, two), 这里有 32个需要提取的结果, 所以会调用伴生对象.unapplySeq方法.
  2. 如果这个方法返回的是Some, 并且 Some中的序列有 2个值, 则匹配成功. 否则就匹配失败.

1.9 变量声明中的模式

在声明变量的时候,也可以使用模式匹配的方式. (在其他语言中叫做解构)

object VarMatch {
  def main(args: Array[String]): Unit = {
    var (a: Int, b: Int) = (10, 20)
    println(s"a = $a, b=$b")

    val (aa, bb) = BigInt(10) /% 3
    println(s"aa = $aa, bb = $bb")

    val arr = Array(100, 200, 300, 400)
    val Array(c, _, d, _*) = arr  //
    println(s"c = $c, d = $d")
  }
}

在这里插入图片描述

1.10 for表达式中的模式

def main(args: Array[String]): Unit = {
    val map = Map("a" -> 1, "b" -> 2, "c" -> 3, "d" -> 2)

    // 直接将Map中的K-V遍历出来
    for ((k, v) <- map) {
      println(s"k = $k, v = $v")
    }
    println("--------------")
    // 只遍历 v = 2的 k-v
    for((k, 2) <- map) {
      println(s"k = $k")
    }
    println("--------------")
    // 也可以使用 守卫: 遍历v > 1的
    for ((k, v) <- map if v > 1){
      println(s"k = $k, v = $v")
    }
  }

1.11 样例类

案例1
object CaseClassDemo1 {
  def main(args: Array[String]): Unit = {
    val arr = Array(Dollar(1000), Currency(10000, "RMB"))

    for (ele <- arr) {
      val res = ele match {
        case Dollar(v) => "$" + v
        case Currency(v, u) => v + u
        case _ => ""
      }
      println(res)
    }
  }
}

// 一个抽象类
abstract class Amount {}

// Dollar: 样例类 继承自 Amount
case class Dollar(value: Double) extends Amount {}

// Currency: 样例类
case class Currency(value: Double, unit: String) {}

在这里插入图片描述

案例2

copy会创建一个与现有对象值相同的新对象,并可以通过带名参数来修改某些属性。

object CaseClassDemo2 {
  def main(args: Array[String]): Unit = {
    val amt1: Currency = Currency(122.2, "美元")
    // 复制一个与 amt1 完全相同的对象
    val amt2: Currency = amt1.copy()
    val amt3: Currency = amt1.copy(value = 222.2)
    val amt4: Currency = amt1.copy(unit = "英镑")
    println(amt1)
    println(amt2)
    println(amt3)
    println(amt4)
  }
}

在这里插入图片描述

案例3

在这里插入图片描述

1.12 case语句中的中置表示法

![](W:\博客\博客图片\2019-01-16_153746.png)

1.13 匹配嵌套结构

object MatchNest {
  def main(args: Array[String]): Unit = {
    val book1 = Book("九阳真经", 100)
    val book2 = Book("葵花宝典", 120)
    val bundle1 = Bundle("书籍", 20, book1, book2)

    println(price2(book1))
    println(price2(book2))
    println(price2(bundle1))

  }

  /**
    * 计算打包的销售的书的价格
    *
    * 方式1:
    */
  def price(item: Item): Double = {
    val money = item match {
      case Book(_, price) => price
      case Bundle(_, discount, items@_*) => {
        var sum = -discount
        for (elem <- items) {
          sum += price(elem)
        }
        sum
      }
      case _ => 0
    }
    money
  }
  // 方式2
  def price2(item: Item): Double = {
    item match {
      case Book(_, price) => price
      case Bundle(_, discount, items@_*) => {
        // 不用循环
        // 把每本书的价格映射到新的序列中, 然后再求和
        items.map(price2).sum - discount
      }
      case _ => 0
    }
  }
}

// 抽象类
abstract class Item

// 样式类 Book
case class Book(description: String, price: Double) extends Item

// 捆绑的样式类
case class Bundle(description: String, discount: Double, item: Item*) extends Item

1.4 密封类

package com.liuser.scala.note

object MatchSeal {

    def main(args: Array[String]): Unit = {
        val amounts = Array(Dollar1(100),Dollar1(200), new Currency1(100,"rmb"))

        for (elem <- amounts) {
            elem match {
                case Dollar1(v) => println("values=" + v)
                case Currency1(v,u)=>println("v=" +v ,"u="+u)
                case _ => println("匹配失败")
            }
        }

    }
}
//密封类
sealed abstract class Amount1{}

case class Dollar1(value:Double) extends Amount1{}

case class Currency1(value:Double,unit : String) extends Amount1{}

1.5 模拟枚举类

object EnumDemo {
  def main(args: Array[String]): Unit = {
    val seasons = Array(Spring, Summer, Autumn, Winter)
    for (season <- seasons) {
      val msg = season match {
        case Spring => "春天"
        case Summer => "夏天"
        case Autumn => "秋天"
        case Winter => "冬天"
        case _ => "每在地球上"
      }
      println(msg)
    }
  }
}

sealed abstract class Season

case object Spring extends Season

case object Autumn extends Season

case object Summer extends Season

case object Winter extends Season
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值