Scala之模式匹配

Scala之模式匹配

一、概述

Scala中的模式匹配类似于Java中的switch语法,但是更加强大。
模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有case都不匹配,那么会执行case _分支,类似于Java中default语句。

二、基本语法

package com.scala.charpter08

import scala.io.StdIn

object $01_MathDefine {
  
  /**
   * 模式匹配语法:
   *    变量  match{
   *        case  条件1  => {
   *            ....
   *         }
   *        case  条件1  => {
   *             ....
   *         }
   *         .....
   *    }
   *
   *    模式匹配有返回值,返回值为符合条件的分支的{}中最后一个表达式的结果值
   * @param args
   */
  def main(args: Array[String]): Unit = {
    
    val  word = StdIn.readLine("请输入一个单词")
    
    val result = word match {
      case  "hadoop" =>
        println("单词是hadoop")
        println("...........")
        10
      case  "spark" =>
        println("单词是spark")
        println("...........")
        20
      case x=>
        println(s"单词是${x}")   
    }
    
    println(result) 

  } 
}

说明

(1)如果所有case都不匹配,那么会执行case _ 分支,类似于Java中default语句,若没有case _ 分支,那么会抛出MatchError。
(2)每个case中,不用break语句,自动中断case。
(3)match case语句可以匹配任何类型,而不只是字面量。
(4)=> 后面的代码块,是作为一个整体执行,可以使用{}括起来,也可以不括。

三、模式守卫

package com.scala.charpter08

import scala.io.StdIn

object $02_MathIf {
  
  /**
   * 模式匹配守卫:
   *     变量  match {
   *         case  条件1  if  (布尔表达式) =>{
   *             .....
   *          }
   *         case  条件1  if  (布尔表达式) =>{
   *              .....
   *           }
   *           .....
   *      }
   * @param args
   */
  def main(args: Array[String]): Unit = {
  
    val line = StdIn.readLine("请输入一个单词:")
  
    line match {
      case x if(x.contains("hadoop"))=>
        println("单词是hadoop")
      case x if(x.contains("spark"))=>
        println("单词是spark")
      case _=>
        println("其他。。。")     
    }
   
  }
}

说明

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

四、模式匹配类型

4.1 匹配常量

object TestMatchVal {

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

    println(describe(6))

  }


  def describe(x: Any) = x match {

    case 5 => "Int five"

    case "hello" => "String hello"

    case true => "Boolean true"

    case '+' => "Char +"

  }

}

说明

scala中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等。

4.2 匹配类型

package com.scala.charpter08

import scala.util.Random

object $03_MatchType {
  
  def main(args: Array[String]): Unit = {
    
    val list: List[Any] = List(2,9.0,false,"hello")
    
    val index: Int = Random.nextInt(list.length)
    
    val parm = list(index)
    println(parm)
    
    parm match {
      case _:String=>
        println("string.....")
      case _:Int=>
        println("Int......")
      case _:Double=>
        println("double....")
      case _=>
        println("其他。。。")    
    }
   
  } 
}

说明

需要进行类型判断时,可以使用前文所学的isInstanceOf[T]和asInstanceOf[T],也可使用模式匹配实现同样的功能。

4.3 匹配数组

package com.scala.charpter08

object $04_MatchArray {
  
  def main(args: Array[String]): Unit = {
    
    val arr:Array[Any] = Array(78)
    
    arr match {
      
      //匹配数组只有一个元素的情况
      case Array(x)=>
        println(s"数组只有一个元素${x}")
      
      //匹配数组有四个元素的情况,类型为。。。
      case Array(x:Int,y:Double,a,z:String)=>
        println(s"数组有四个元素,类型分别为。。。。。。${x}")
      
      //匹配数组有四个元素
      case Array(x,y,a,z)=>
        println(s"数组有四个元素,类型分别为。。。。。。${x}")
      
      //匹配数组至少有一个元素
      case Array(x,_*)=>
        println("数组至少有一个元素。。。")
     
      //其他情况
      case _=>
        println("其他。。。")   
    }
    
  } 
}

说明

scala模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为0的数组。

4.4 匹配列表

package com.scala.charpter08

object $05_MatchList {
  
  def main(args: Array[String]): Unit = {
  
    val list: List[Any] = List(1,8.0,false,"scala",11)
    
   	 //与Array类似进行匹配
     list match {
      
	      //匹配列表只有一个元素的情况
	      case List(x)=>
	        println(s"数组只有一个元素${x}") 
	      //匹配列表有四个元素的情况,类型为。。。
	      case List(x:Int,y:Double,a,z:String)=>
	        println(s"数组有四个元素,类型分别为。。。。。。${x}")
	      //匹配列表有四个元素
	      case List(x,y,a,z)=>
	        println(s"数组有四个元素,类型分别为。。。。。。${x}")
	      //匹配列表至少有一个元素
	      case List(x,_*)=>
	        println("数组至少有一个元素。。。")   
	      //其他情况
	      case _=>
	        println("其他。。。")
        
    }
      
    
    list match {
  
	      case x::Nil=>
	        println("列表只有一个元素")
	      case (x:String)::y::(z:Int)::a::Nil=>
	        println("列表有四个元素,类型分别为。。。。")
	      case x::y::z::a::Nil=>
	        println("列表有四个元素")
	      case x::tail=>
	        println(s"列表至少有一个元素${x} ${tail}")
	      case _=>
	        println("其他")
    }
     
    
    val list2:List[Any] = List(1,2,3,4)
    //泛型擦除,集合的泛型主要用来在代码编写期间规定集合中数据元素的类型
    //泛型在编译的时候会被擦掉,任何list都能匹配上
    list match {
      case x:List[Boolean]=>
        println("boolean....")
      case x:List[String]=>
        println("string.....")
      case x:List[Int]=>
        println("int........")
      case x:List[Any]=>
        println("any....")
      case _=>
        println("........")      
    }
   
  }
}

4.5 匹配元组

package com.scala.charpter08

object $06_MatchTope {
  
  def main(args: Array[String]): Unit = {
    
    val t1:(Any,Any,Any)=("zhangsna","20","shezhen")
    
    t1 match {
      case (x:String,y:Int,z:String)=>
        println("...........")
      case (_,_,_)=>
        println("元素个数是3个")
      case _=>
        println("其他")
    }
  
  
    val list = List(
      ("宝安",("宝安中学",("大神班",("zhangsan",20)))) ,
      ("宝安",("宝安中学",("大神班",("lisi",20)))),
      ("宝安",("宝安中学",("大神班",("wangwu",20)))),
      ("宝安",("宝安中学",("大神班",("zhaoliu",20))))
    )
    
    list.map(x=>{
      x match {
        case (region,(school, (className,(stuName,age))))=>
        stuName
      }
    }).foreach(println)
 
  } 
}

4.6 匹配对象及样例类

package com.scala.charpter08

object $07_MatchClass {
  
  case class Person(val name:String,var age:Int,addr: String)
  
  class Student(val name:String,var age:Int,val addr:String)
  
  object Student{
  
    def apply(name: String, age: Int, addr: String): Student = new Student(name, age, addr)
  
    def unapply(stu: Student): Option[(String, Int, String)] = {
      if(stu==null)
        None
      else
        Some((stu.name,stu.age,stu.addr))      
      
    }
  
  }
  
  
  abstract class Sex
  
  case object Man extends Sex
  
  case object Woman extends Sex
  
  def xx(sex:Sex)={
    println(sex)
  }
  
  /**
   * 样例类:
   *    语法:case class 类名([var/val] 属性名:类型,....)
   * 创建样例类对象:类名(属性值,....)
   * 属性不用var/val修饰时默认就是val
   * 样例类其实就是伴生对象和伴生类的简写
   *
   * 样例对象:
   *    语法:case  object  object名称
   * 一般用于枚举
   *
   *
   * 普通类不能直接用于模式匹配,要想用于模式匹配需要在伴生对象中添加unapply方法
   *
   * @param args
   */
  def main(args: Array[String]): Unit = {
  
    val person = Person("zhangsan",20,"shenzhen")
    println(person.name)
    println(person.addr)
    println(person.age)
    
    person.age=90
    println(person.age)
  
//    person.addr = "kkk"
  
    val lisi: Student = Student("lisi",9,"shanghai")
    lisi.age=990
    println(lisi.name)
    println(lisi.age)
  
  
    xx(Man)
  
  
    person match {
      case Person(x,y,z)=>
        println(x,y,z)
        
    }
   
    val sudent = new Student("wangwu",44,"ilovyou")
    
    sudent match {
      case Student(x,y,z)=>
        println(x,y,z)
    } 
    
  } 
}

五、变量声明中的模式匹配

case class Person(name: String, age: Int)

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

    val (x, y) = (1, 2)
    println(s"x=$x,y=$y")

    val Array(first, second, _*) = Array(1, 7, 2, 9)
    println(s"first=$first,second=$second")

    val Person(name, age) = Person1("zhangsan", 16)
    println(s"name=$name,age=$age")
  }
}

六、for表达式中的模式匹配

object TestMatchFor {

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

    val map = Map("A" -> 1, "B" -> 0, "C" -> 3)
    for ((k, v) <- map) { //直接将map中的k-v遍历出来
      println(k + " -> " + v) //3个
    }
    println("----------------------")

    //遍历value=0的 k-v ,如果v不是0,过滤
    for ((k, 0) <- map) {
      println(k + " --> " + 0) // B->0
    }

    println("----------------------")
    //if v == 0 是一个过滤的条件
    for ((k, v) <- map if v >= 1) {
      println(k + " ---> " + v) // A->1 和 c->33
    }
  }
}

七、偏函数中的模式匹配

偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为List[Int],而我们需要的是第一个元素是0的集合,这就是通过模式匹配实现的。

1)偏函数定义

val second: PartialFunction[List[Int], Option[Int]] = {
  case x :: y :: _ => Some(y)
}

注:该偏函数的功能是返回输入的List集合的第二个元素

2)偏函数原理

上述代码会被scala编译器翻译成以下代码,与普通函数相比,只是多了一个用于参数检查的函数——isDefinedAt,其返回值类型为Boolean。

  val second = new PartialFunction[List[Int], Option[Int]] {

    //检查输入参数是否合格
    override def isDefinedAt(list: List[Int]): Boolean = list match {
      case x :: y :: _ => true
      case _ => false
    }

    //执行函数逻辑
    override def apply(list: List[Int]): Option[Int] = list match {
      case x :: y :: _ => Some(y)
    }
  }

3)偏函数使用

偏函数不能像second(List(1,2,3))这样直接使用,因为这样会直接调用apply方法,而应该调用applyOrElse方法,如下

second.applyOrElse(List(1,2,3), (_: List[Int]) => None)

applyOrElse方法的逻辑为 if (ifDefinedAt(list)) apply(list) else default。如果输入参数满足条件,即isDefinedAt返回true,则执行apply方法,否则执行defalut方法,default方法为参数不满足要求的处理逻辑。

4)案例实操
(1)需求

将该List(1,2,3,4,5,6,“test”)中的Int类型的元素加一,并去掉字符串。

(2)实操

//方法一:
List(1,2,3,4,5,6,"test").filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int] + 1).foreach(println)
//方法二:
List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(println)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值