大数据——Scala 模式匹配

Scala

模式匹配

概述

  1. Scala中的模式匹配,类似于Java中switch-case结构,但是比Java中的switch-case的功能更加强大

  2. 语法结构

    scala 复制代码
    选项 match {
        case c1 => op1
        case c2 => op2
        ...
        case _ => op
    }
  3. case _类似于Java中的default,当其他的所有的case都不匹配的时候,才会使用case _

  4. 在Scala中,没有break关键字,每一个case执行完成之后自动结束,不会顺延执行下一个case

  5. 案例

    scala 复制代码
    package com.fe.matchx
    
    import scala.io.StdIn
    
    object MatchDemo1 {
    
      def main(args: Array[String]): Unit = {
    
        // 获取符号
        val symbol = StdIn.readChar()
        // 获取数字
        val x = StdIn.readDouble()
        val y = StdIn.readDouble()
        // 四则运算
        val r = symbol match {
          case '+' => x + y
          case '-' => x - y
          case '*' => x * y
          case '/' => x / y
          case _ => 0
        }
        println(r)
      }
    
    }
  6. 如果没有case _,且所有的case都不匹配的时候,就会抛出MatchError

常见匹配

  1. 常量匹配:在Scala中,match-case匹配的时候,不限制元素的类型

    scala 复制代码
    package com.fe.matchx
    
    object MatchDemo2 {
    
      def main(args: Array[String]): Unit = {
    
        // Scala中,match不限制元素的类型
        def typeOf(x: Any): Unit = x match {
          case 3 => println("整数")
          case 5.5 => println("小数")
          case true => println("布尔值")
          case "abc" => println("字符串")
          case _ => println("其他类型")
        }
    
        val t = (3, true, "abc", 5.5, 'a')
        t.productIterator.foreach(x => typeOf(x))
    
      }
    
    }
  2. 类型匹配

    scala 复制代码
    package com.fe.matchx
    
    object MatchDemo3 {
    
      def main(args: Array[String]): Unit = {
    
        // 判断元组中每一个元素的类型
        val t = (4, 5.5, 6, true, 'a', "abc", 3.5f, 2L)
    
        def typeOf(x: Any): String = x match {
          case _: Int => "Int"
          case _: Double => "Double"
          case _: String => "String"
          case _: Long => "Long"
          case _: Boolean => "Boolean"
          case _: Char => "Char"
          case _ => "other type"
        }
    
        t.productIterator.foreach(x => println(typeOf(x)))
    
      }
    
    }
  3. 集合匹配

    scala 复制代码
    package com.fe.matchx
    
    object MatchDemo4 {
    
      def main(args: Array[String]): Unit = {
    
        // 对列表中的元素类型进行匹配
        def listType(list: List[_]) = list match {
          case _: List[Int] => "Int"
          case _: List[Double] => "Double"
          case _ => "other type"
        }
    
        val list = List(2, 3, 4, 5)
        println(listType(list))
    
        // 对列表中的元素进行限定
        def listMatch(list: List[_]) = list match {
          case List(3) => "List(3)" // 固定匹配,就匹配这个List(3)
          case List(2, 4, 6) => "List(2,4,6)" // 固定匹配,就匹配这个List(2, 4, 6)
          case List(_, _) => "长度为2的List" // 匹配长度为2的列表
          case List("abc", _, _) => "列表的长度为3,第一个元素是abc" // 匹配列表的长度为3,并且第一个元素是"abc"
          case List("hello", _*) => "第一个元素是hello的列表" // 匹配第一个元素是"hello"的列表
        }
    
        val list1 = List(3)
        println(listMatch(list1))
        val list2 = List(2, 4, 6)
        println(listMatch(list2))
        val list3 = List('a', 'b')
        val list4 = List(3.5, 4.8)
        println(listMatch(list3))
        println(listMatch(list4))
        val list5 = List("abc", "hello", "hi")
        val list6 = List("abc", "hadoop", "Scala")
        println(listMatch(list5))
        println(listMatch(list6))
        val list7 = List("hello", "hi", "system")
        val list8 = List("hello")
        val list9 = List("hello", "david", "amy", "bob")
        println(listMatch(list7))
        println(listMatch(list8))
        println(listMatch(list9))
    
      }
    
    }

模式守卫

  1. 如果在进行匹配的时候,还需要指定其他条件,那么可以使用模式守卫

    scala 复制代码
    package com.fe.matchx
    
    object MatchDemo5 {
    
      def main(args: Array[String]): Unit = {
    
        // 判断数字是几位数
        def test(n: Int) = n match {
          case _: Int if n < 0 => "负数"
          case _: Int if n < 10 => "一位数"
          case _ => "两位数"
        }
    
        println(test(5))
    
        // 列表
        def listMatch(list: List[Int]) = list match {
          case x if x.length == 3 && x.contains(5) => "case 1" // 列表长度为3,且包含数字5的列表
          case y if y.contains(7) => "case 2" // 列表中包含数字7
        }
    
        val list1 = List(3, 1, 5)
        val list2 = List(5, 2, 4)
        println(listMatch(list1))
        println(listMatch(list2))
        val list3 = List(7, 1, 3, 4)
        println(listMatch(list3))
    
      }
    
    }

函数参数匹配

  1. 案例

    scala 复制代码
    package com.fe.matchx
    
    object MatchDemo6 {
    
      def main(args: Array[String]): Unit = {
    
        // toInt ===> AnyVal
        val values: List[Any] = List(true, 1, 3.5, "25.53", -4, "35", 'a', false, -6.5)
        // 将列表中的所有的元素转化为整数
        // 如果是布尔值,那么true转化为1,false转化为0
        // 如果是字符串,那么就转化为对应的值
        val r = values.map(x => x match {
          case a: Double => a.toInt
          case b: String => b.toDouble.toInt
          case c: Char => c.toInt
          case d: Boolean => if (d) 1 else 0
          case e: Int => e
        })
        println(r)
        // 对参数进行匹配,并且在这个过程中,是直接去进行了match操作,而没有进行其他操作
        // ({}),如果代码只有一行,()或者{}可以省略其一;如果有多行,那么只能考虑省略()
        //
        val r2 = values.map {
          case a: Double => a.toInt
          case b: String => b.toDouble.toInt
          case c: Char => c.toInt
          case d: Boolean => if (d) 1 else 0
          case e: Int => e
        }
        println(r2)
      }
    
    }

对象匹配

  1. Scala中不只是可以对数据进行匹配,还能对对象进行匹配,到那时要求对应对应的类中必须提供了解析函数unapply

    scala 复制代码
    package com.fe.matchx
    
    object MatchDemo7 {
    
      def main(args: Array[String]): Unit = {
    
        val p1 = Person("Bob", 21, "male")
        val p2 = Person("Amy", 22, "female")
        val p3 = Person("David", 22, "male")
        val p4 = Person("Grace", 23, "female")
        val p5 = Person("Henry", 21, "male")
        val persons = List(p1, p2, p3, p4, p5)
    
        // 对集合中数据进行操作
        def testList(p: Person) = p match {
          case Person(_, _, "female") => "女性用户" // 目标客户:女性
          case Person(_, 21, _) => "21岁客户群体" // 目标客户:21岁群体
          case _ =>
        }
    
        persons.foreach(x => println(testList(x)))
      }
    
    }
    
    class Person {
      var username: String = _
      var age: Int = _
      var gender: String = _
    }
    
    object Person {
    
      def apply(username: String, age: Int, gender: String): Person = {
        val p = new Person
        p.username = username
        p.age = age
        p.gender = gender
        p
      }
    
      // 解析函数,用于解析对象
      // 在Scala中,为了防止空指针异常,会考虑将结果封装成Option
      // 在封装Option的时候,如果值为空,对应的封装成None
      // 如果值不为空,封装成Some
      def unapply(p: Person): Option[(String, Int, String)] = {
        if (p == null) None
        else Some(p.username, p.age, p.gender)
      }
    }

样例类

  1. 样例类的声明和普通类差别不大,区别在于,在class之前添加了case

    scala 复制代码
    case class 类名 {}
  2. 样例类的普通类的区别

    1. 如果一个类被声明为样例类,那么Scala在编译的时候会自动的给这个样例类生成一个伴生对象;普通类需要手动声明伴生对象
    2. 样例类对应的伴生对象中,会自动覆盖applyunapplytoStringequalshashCode
    3. 也正因为样例类中会自动覆盖apply和unapply函数,所以可以参与模式匹配
    4. 样例类的主构造器中声明的参数默认是val修饰
  3. 案例

    scala 复制代码
    package com.fe.matchx
    
    object CaseClassDemo {
    
      def main(args: Array[String]): Unit = {
    
        val s1 = Student("Bob", 10, "male", 3)
        val s2 = Student("Tom", 10, "male", 3)
        val s3 = Student("Sam", 10, "male", 3)
        val s4 = Student("Cam", 10, "female", 3)
        val s5 = Student("Amy", 10, "female", 3)
        val s6 = Student("Dan", 10, "male", 3)
        val students = List(s1, s2, s3, s4, s5, s6)
        for (s <- students) {
          val r = s match {
            case Student(_, _, "male", _) => "男生"
            case _ => "女生"
          }
          println(r)
        }
    
      }
    
    }
    
    // 自动给Student生成伴生对象`object Student`
    case class Student(name: String, age: Int, gender: String, grade: Int) {
      // 声明在类中的属性,默认不是被apply接收和unapply解析
      // var address:String = _
    }
    
    /*
    object Student{
    
      def apply(name: String, age: Int, gender: String, grade: Int):Student = new Student(name, age, gender, grade)
      def unapply(s:Student) = Some(u.name, u.age, u.gender, u.grade)
    
    }
     */

偏函数

  1. 对于Scala的集合函数而言,分为全函数和偏函数

    1. 全函数:对集合中所有的元素进行操作,例如mapreduce
    2. 偏函数:对集合中的部分元素进行操作
  2. 案例

    scala 复制代码
    package com.fe.matchx
    
    object MatchDemo8 {
    
      def main(args: Array[String]): Unit = {
    
        val list: List[Any] = List(2, 'a', 6, 3.5, true, 8, 5.21, 9, false, "abc")
        // 需求:将列表中的整数挑选出来,计算整数的平方形式
        // 方式一:过滤 -> 转化为整数 -> 计算平方
        val r = list.filter(_.isInstanceOf[Int])
          .map(_.asInstanceOf[Int])
          .map(x => x * x)
        println(r)
        // 方式二:偏函数
        val r2 = list.collect { case a: Int => a * a }
        println(r2)
    
      }
    
    }

异常机制

  1. Scala中的异常机制,类似于Java中的异常机制,但是又不完全一样。Scala中所有的异常都是在运行的时候才会检查,没有所谓的编译时异常

  2. Scala捕获异常的方式

    scala 复制代码
    try{
        代码块
    } catch {
        case e1:异常名 => 处理
        case e2:异常名 => 处理
        ...
    } finally{
        代码块
    }
  3. 案例

    scala 复制代码
    package com.fe.exception
    
    import scala.io.StdIn
    
    object ExceptionDemo {
    
      def main(args: Array[String]): Unit = {
    
        try {
          val x = StdIn.readInt()
          val y = StdIn.readInt()
          println(x / y)
        } catch {
          case e: ArithmeticException => println("捕获到一个算术异常")
        }
    
      }
    
    }
  4. 在Scala中,如果一个函数没有别的返回值,而是只抛出了一个异常,那么此时这个函数的返回值类型是Nothing

    scala 复制代码
    def testException():Nothing ={
        throw new IllegalArgumentException
    }
  5. Scala中提供了一个注解throws,用于标记这个函数有异常抛出。这个注解仅仅起提示作用,不要求使用者必须处理这个异常

隐式转换(implicit)

概述

  1. 当编译器对当前代码第一次编译失败的时候,会在当前的环境中查找能够让代码编译通过的方式,用于将当前的类型进行转换,进行二次编译,这个过程就称之为隐式转换
  2. 隐式转换包含隐式函数、隐式参数和隐式类
  3. Scala运行的时候,自动加载Predef类,Predef类中定义了大量的隐式转换

隐式函数

  1. 隐式函数,能够在不改变某一个类的前提下,扩展这个类的功能

  2. 案例

    scala 复制代码
    package com.fe.implicitx
    
    object ImplicitDemo {
    
      def main(args: Array[String]): Unit = {
    
    
        // 正常情况下,x是Int类的对象,所以无法调用SumInt中的函数
        val x: Int = 10
        // 在没法改变Int类的前提下,又想扩展Int的功能
        // 此时,就意味着需要将Int -> SumInt对象之后才能调用
        // 此时可以定义一个隐式函数来完成这个转换过程
        // 在底层,偷偷的将Int包装成了SumInt
        implicit def transform(n:Int):SumInt = new SumInt(n)
    
        println(x.sum)
        println(x.^(5))
    
      }
    
    }
    
    class SumInt(val n: Int) {
    
      def sum: Int = {
        println("我来啦~~~")
        var r = 0
        for (i <- 1 to n) r += i
        r
      }
    
    }

隐式参数

  1. 隐式参数,指的是在作用域中定义的可以被自动匹配的参数

  2. 注意:

    1. 同一个作用域中,相同类型的隐式参数只能有1个
    2. 编译器是根据参数类型匹配,而不是根据参数名称匹配
  3. 案例

    scala 复制代码
    package com.fe.implicitx
    
    object ImplicitDemo2 {
    
      def main(args: Array[String]): Unit = {
    
        implicit val s: String = "男"
    
        // 性别默认为"男",除非用户指定为"女"
        // 第三个参数声明为隐式参数,所以如果调用的时候不给定,自动的在当前作用域内自动寻找同类型的隐式变量
        def register(username: String)(age: Int)(implicit gender: String): Unit = println(s"新注册用户$username,性别$gender,年龄$age")
        // 隐式参数的优先级高于默认参数
        def login(username: String)(implicit gender: String = "女"): Unit = println(s"用户$username,性别$gender 登陆")
    
        register("Bob")(15)
        register("David")(16)
        login("Bob")
        login("Any")("女")
    
      }
    
    }

隐式类

  1. 如果需要对某一个类产生的对象进行功能扩展,而又不想要改变原来的类,那么除了可以使用隐式函数,还可以使用隐式类

  2. 注意:隐式类不是顶级类,即隐式类不能直接定义到包中,而必须定义到类、伴生对象或者包对象中

  3. 饮食类必须提供构造器,并且构造器中只能有1个参数!

    scala 复制代码
    package com.fe.implicitx
    
    object ImplicitDemo3 {
    
      def main(args: Array[String]): Unit = {
    
        val x: Int = 5
        println(x.fac)
    
      }
    
      implicit class FacInt(n: Int) {
    
        def fac: Int = {
          var r = 1
          for (i <- 1 to n) r *= i
          r
        }
      }
    
    }
相关推荐
Adolf_19938 分钟前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
叫我:松哥20 分钟前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
海里真的有鱼22 分钟前
Spring Boot 项目中整合 RabbitMQ,使用死信队列(Dead Letter Exchange, DLX)实现延迟队列功能
开发语言·后端·rabbitmq
txtsteve24 分钟前
es由一个集群迁移到另外一个集群es的数据迁移
大数据·elasticsearch·搜索引擎
工作中的程序员27 分钟前
ES 索引或索引模板
大数据·数据库·elasticsearch
工业甲酰苯胺33 分钟前
Spring Boot 整合 MyBatis 的详细步骤(两种方式)
spring boot·后端·mybatis
新知图书1 小时前
Rust编程的作用域与所有权
开发语言·后端·rust
wn5312 小时前
【Go - 类型断言】
服务器·开发语言·后端·golang
希冀1232 小时前
【操作系统】1.2操作系统的发展与分类
后端
GoppViper3 小时前
golang学习笔记29——golang 中如何将 GitHub 最新提交的版本设置为 v1.0.0
笔记·git·后端·学习·golang·github·源代码管理