大数据——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
        }
      }
    
    }
相关推荐
why15113 分钟前
腾讯(QQ浏览器)后端开发
开发语言·后端·golang
浪裡遊17 分钟前
跨域问题(Cross-Origin Problem)
linux·前端·vue.js·后端·https·sprint
声声codeGrandMaster24 分钟前
django之优化分页功能(利用参数共存及封装来实现)
数据库·后端·python·django
呼Lu噜1 小时前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
bing_1581 小时前
为什么选择 Spring Boot? 它是如何简化单个微服务的创建、配置和部署的?
spring boot·后端·微服务
学c真好玩1 小时前
Django创建的应用目录详细解释以及如何操作数据库自动创建表
后端·python·django
Asthenia04121 小时前
GenericObjectPool——重用你的对象
后端
Piper蛋窝1 小时前
Go 1.18 相比 Go 1.17 有哪些值得注意的改动?
后端
excel2 小时前
招幕技术人员
前端·javascript·后端
望获linux2 小时前
智能清洁机器人中的实时操作系统应用研究
大数据·linux·服务器·人工智能·机器人·操作系统