• 大数据——Scala 模式匹配


    Scala

    模式匹配

    概述

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

    2. 语法结构

      选项 match {
          case c1 => op1
          case c2 => op2
          ...
          case _ => op
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    3. case _类似于Java中的default,当其他的所有的case都不匹配的时候,才会使用case _

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

    5. 案例

      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)
        }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
    6. 如果没有case _,且所有的case都不匹配的时候,就会抛出MatchError

    常见匹配

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

      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))
      
        }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    2. 类型匹配

      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)))
      
        }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
    3. 集合匹配

      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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47

    模式守卫

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

      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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31

    函数参数匹配

    1. 案例

      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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33

    对象匹配

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

      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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50

    样例类

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

      case class 类名 {}
      
      • 1
    2. 样例类的普通类的区别

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

      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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39

    偏函数

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

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

      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
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20

    异常机制

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

    2. Scala捕获异常的方式

      try{
          代码块
      } catch {
          case e1:异常名 => 处理
          case e2:异常名 => 处理
          ...
      } finally{
          代码块
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    3. 案例

      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("捕获到一个算术异常")
          }
      
        }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
    4. 在Scala中,如果一个函数没有别的返回值,而是只抛出了一个异常,那么此时这个函数的返回值类型是Nothing

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

    隐式转换(implicit)

    概述

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

    隐式函数

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

    2. 案例

      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
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32

    隐式参数

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

    2. 注意:

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

      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
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22

    隐式类

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

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

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

      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
          }
        }
      
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
  • 相关阅读:
    @Scope 注解失效了?咋回事
    Failed to launch task: 文件”Setup”不存在 Mac安装Adobe软件报错解决方案
    3dmax如何进行网络渲染?网渲云渲染渲染农场怎么用?
    JUL日志讲解
    【SpringMVC】之自定义注解
    什么年代了,还在用FastQC?试试Falco吧
    【MySQL】# 自定义变量、一行数据与多行的转换、IF函数
    盘点10个地推拉新和网推拉新app推广接单平台,免费一手渠道平台
    4 JavaScript
    echarts实现折线图点击添加标记
  • 原文地址:https://blog.csdn.net/weixin_51401546/article/details/137968738