二维码
微世推网

扫一扫关注

当前位置: 首页 » 快报资讯 » 今日快报 » 正文

Scala_模式匹配详解

放大字体  缩小字体 发布日期:2022-01-05 04:10:28    作者:田宇雪    浏览次数:350
导读

大数据技术AI Flink/Spark/Hadoop/数仓,数据分析、面试,源码解读等干货学习资料 101篇来自互联网内容 --公众号Scala 中得模式匹配类似于 Java 中得 switch 语法 int i = 10 switch (i) {case 10 : System.out.println("10"); break;case 20 :System.out.println("20"); break;default :System.out.printl

大数据技术AI Flink/Spark/Hadoop/数仓,数据分析、面试,源码解读等干货学习资料 101篇来自互联网内容 -->

公众号

Scala 中得模式匹配类似于 Java 中得 switch 语法

int i = 10 switch (i) { case 10 : System.out.println("10"); break; case 20 : System.out.println("20"); break; default : System.out.println("other number"); break; }

但是 scala 从语法中补充了更多得功能,所以更加强大。

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

    val a = 25 val b = 13 def matchDualOp(op: Char): Int = op match { case '+' => a + b case '-' => a - b case '*' => a * b case '/' => a / b case '%' => a % b case _ => -1 } println(matchDualOp('+')) println(matchDualOp('/')) println(matchDualOp('\\'))

  • 说明:

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

    若此时没有 case _ 分支,那么会抛出 MatchError。

    (2)每个 case 中,不需要使用 break 语句,自动中断 case。

    (3)match case 语句可以匹配任何类型,而不只是字面量。

    (4)=> 后面得代码块,直到下一个 case 语句之前得代码是作为一个整体执行,

    可以 使用{}括起来,也可以不括。

    2 模式守卫
  • 说明

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

  • 案例实操

    def abs(num: Int): Int = { num match { case i if i >= 0 => i case i if i < 0 => -i } } println(abs(67))3 模式匹配类型3.1 匹配常量

    def test(x: Any): String = x match { case 1 => "Int one" case "hello" => "String hello" case true => "Boolean true" case '+' => "Char +" case _ => "" } println(test("hello")) println(test('+')) println(test(0.3))

    String hello Char +3.2 匹配类型

    需要进行类型判断时,可以使用前文所学得 isInstanceOf[T]和 asInstanceOf[T],也可使

    用模式匹配实现同样得功能。

    def test(x: Any): String = x match { case i: Int => "Int " + i case s: String => "String " + s //case list: List[String] => "List " + list case array: Array[Int] => "Array[Int] " + array.mkString(",") //泛型擦除 case m: List[_] => "List"+m case a => "else: " + a } println(test(35)) println(test("hello")) //泛型擦除 println(test(List("hi", "hello"))) //泛型擦除 println(test(List(2, 23))) println(test(Array("hi", "hello"))) println(test(Array(2, 23)))

    Int 35 String hello List List(hi, hello) List List(2, 23) else: [Ljava.lang.String;等6e8dacdf Array[Int] 2,233.3 匹配数组

    scala 模式匹配可以对集合进行精确得匹配,例如匹配只有两个元素得、且第壹个元素

    为 0 得数组。

    for (arr <- List( Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(2, 3, 7, 15), Array("hello", 1, 30) )) { val result = arr match { case Array(0) => "0" case Array(1, 0) => "Array(1, 0)" // 匹配两元素数组 case Array(x, y) => "Array: " + x + ", " + y case Array(0, _*) => "以0开头得数组" case Array(x, 1, z) => "中间为1得三元素数组" case _ => "else" } println(result) }

    0 Array(1, 0) 以0开头得数组 中间为1得三元素数组 else 中间为1得三元素数组3.4 匹配列表

    方式一:

    for (list <- List( List(0), List(1, 0), List(0, 0, 0), List(1, 1, 0), List(88), List("hello") )) { val result = list match { case List(0) => "0" case List(x, y) => "List(x, y): " + x + ", " + y case List(0, _*) => "List(0, ...)" case List(a) => "List(a): " + a case _ => "else" } println(result) }

    0 List(x, y): 1, 0 List(0, ...) else List(a): 88 List(a): hello

    方式二:

    val list = List(1, 2, 5, 7, 24) //val list = List(24) list match { case first :: second :: rest => println(s"first: $first, second: $second, rest: $rest") case _ => println("else") }

    first: 1, second: 2, rest: List(5, 7, 24)3.5 匹配元组

    for (tuple <- List( (0, 1), (0, 0), (0, 1, 0), (0, 1, 1), (1, 23, 56), ("hello", true, 0.5) )){ val result = tuple match { case (a, b) => "" + a + ", " + b //是第壹个元素是 0 得元组 case (0, _) => "(0, _)" //是第二个元素是 1 得元组 case (a, 1, _) => "(a, 1, _) " + a case (x, y, z) => "(x, y, z) " + x + " " + y + " " + z case _ => "else" } println(result) }

    a 12 b 35 c 27 a 13 a: 12 b: 35 c: 27 a: 133.6 匹配对象

    object test { def main(args: Array[String]): Unit = { val student = Student("法外狂徒,张三", 18) // 针对对象实例得内容进行匹配 val result = student match { case Student("法外狂徒,张三", 18) => "法外狂徒,张三, 18" case _ => "Else" } println(result) } } // 定义类 class Student(val name: String, val age: Int) // 定义伴生对象 object Student { def apply(name: String, age: Int): Student = new Student(name, age) // 必须实现一个unapply方法,用来对对象属性进行拆解 def unapply(student: Student): Option[(String, Int)] = { if (student == null){ None } else { Some((student.name, student.age)) } } }

  • val student = Student("法外狂徒,张三",11),该语句在执行时,实际调用得是 Student 伴生对象中得apply 方法,因此不用 new 关键字就能构造出相应得对象。
  • 当将 Student("法外狂徒,张三", 11)写在 case 后时[case Student("法外狂徒,张三", 11) => "法外狂徒,张三, 18"],会默认调用 unapply 方法(对象提取器),Student 作为 unapply 方法得参数,unapply 方法将 Student 对象得 name 和 age 属性提取出来,与 Student("法外狂徒,张三", 11)中得属性值进行匹配
  • case 中对象得 unapply 方法(提取器)返回 Some,且所有属性均一致,才算匹配成功,属性不一致,或返回 None,则匹配失败。
  • 若只提取对象得一个属性,则提取器为 unapply(obj:Obj):Option[T]
  • 若提取对象得多个属性,则提取器为 unapply(obj:Obj):Option[(T1,T2,T3…)]
  • 若提取对象得可变个属性,则提取器为 unapplySeq(obj:Obj):Option[Seq[T]]3.7 样例类

    object test { def main(args: Array[String]): Unit = { val user = User("zhangsan", 18) // 针对对象实例得内容进行匹配 val result = user match { case User("zhangsan", 18) => "zhangsan, 18" case _ => "Else" } println(result) } } // 定义样例类 case class User(name: String, age: Int)

  • 样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用得方法,如:apply、unapply、toString、equals、hashCode 和 copy。
  • 样例类是为模式匹配而优化得类,因为其默认提供了 unapply 方法,因此,样例类可以直接使用模式匹配,而无需自己实现 unapply 方法。
  • 构造器中得每一个参数都成为 val,除非它被显式地声明为 var(不建议这样做)

    查看字节码文件:

    User$:

    package com.duo; import scala.None.; import scala.Option; import scala.Serializable; import scala.Some; import scala.Tuple2; import scala.runtime.AbstractFunction2; import scala.runtime.BoxesRunTime; public final class User$ extends AbstractFunction2<String, Object, User> implements Serializable{ public static final MODULE$; static { new (); } public final String toString(){ return "User"; } public User apply(String name, int age) { return new User(name, age); } public Option<Tuple2<String, Object>> unapply(User x$0) { return x$0 == null ? None..MODULE$ : new Some(new Tuple2(x$0.name(), BoxesRunTime.boxToInteger(x$0.age()))); } private Object readResolve() { return MODULE$; } private User$() { MODULE$ = this; } }

    User:

    import scala.Function1; import scala.Option; import scala.Product; import scala.Product.class; import scala.Serializable; import scala.Tuple2; import scala.collection.Iterator; import scala.reflect.ScalaSignature; import scala.runtime.BoxesRunTime; import scala.runtime.ScalaRunTime.; import scala.runtime.Statics; 等ScalaSignature(bytes="\006\001\005....") public class User implements Product, Serializable{ private final String name; private final int age; public static Option<Tuple2<String, Object>> unapply(User paramUser) { return User..MODULE$.unapply(paramUser); } public static User apply(String paramString, int paramInt){ return User..MODULE$.apply(paramString, paramInt); } public static Function1<Tuple2<String, Object>, User> tupled() { return User..MODULE$.tupled(); } public static Function1<String, Function1<Object, User>> curried() { return User..MODULE$.curried(); } public String name(){ return this.name; } public int age() { return this.age; } public User copy(String name, int age) { return new User(name, age); } public String copy$default$1() { return name(); } public int copy$default$2() { return age(); } public String productPrefix() { return "User"; } public int productArity() { return 2; } public Object productElement(int x$1) { int i = x$1; switch(i) { default: throw new IndexOutOfBoundsException(BoxesRunTime.boxToInteger(x$1).toString()); case 1: break; case 0: } return name(); } public Iterator < Object > productIterator () { return ScalaRunTime..MODULE$.typedProductIterator(this); } public boolean canEqual(Object x$1) { return x$1 instanceof User; } public int hashCode() { int i = -889275714; i = Statics.mix(i, Statics.anyHash(name())); i = Statics.mix(i, age()); return Statics.finalizeHash(i, 2); } public String toString() { return ScalaRunTime..MODULE$._toString(this); } public boolean equals(Object x$1) { if (this != x$1) { Object localObject = x$1; int i; if ((localObject instanceof User)) i = 1; else i = 0; if (i == 0) break label96; User localUser = (User) x$1; str = localUser.name(); String tmp42_32 = name(); if (tmp42_32 == null) { tmp42_32; if (str == null) break label63; tmpTernaryOp = tmp42_32; break label88; } } } public User (String name, int age) { Product .class.$init$(this); } }4 变量声明中得模式匹配

    val (x, y) = (10, "hello") println(s"x: $x, y: $y") val List(first, second, _*) = List(23, 15, 9, 78) println(s"first: $first, second: $second") val fir :: sec :: rest = List(23, 15 , 9, 78) println(s"first: $fir, second: $sec, rest: $rest")5 for推导式中进行模式匹配

    val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13)) // 1 原本得遍历方式 for (elem <- list){ println(elem._1 + " " + elem._2) } // 2 将List得元素直接定义为元组,对变量赋值 for ((word, count) <- list ){ println(word + ": " + count) } // 3 可以不考虑某个位置得变量,只遍历key或者value for ((word, _) <- list) println(word) // 4 可以指定某个位置得值必须是多少 for (("a", count) <- list){ println(count) } // 5 循环守卫 for ((k, v) <- map if v >= 1) { println(k + " ---> " + v) }6 偏函数中得模式匹配

    偏函数也是函数得一种,通过偏函数我们可以方便得对输入参数做更精确得检查。例如

    该偏函数得输入类型为 List[Int],而我们需要得是第壹个元素是 0 得集合,这就是通过模式

    匹配实现得

    (1)偏函数定义

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

    (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)案例实操

    讲list中元组第二个元素*2

    val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13)) // 1. map转换,实现key不变,value2倍 val newList = list.map( tuple => (tuple._1, tuple._2 * 2) ) // 2. 用模式匹配对元组元素赋值,实现功能 val newList2 = list.map( tuple => { tuple match { case (word, count) => (word, count * 2) } } ) // 3. 省略lambda表达式得写法,进行简化 val newList3 = list.map { case (word, count) => (word, count * 2)}

  •  
    (文/田宇雪)
    免责声明
    • 
    本文仅代表发布者:田宇雪个人观点,本站未对其内容进行核实,请读者仅做参考,如若文中涉及有违公德、触犯法律的内容,一经发现,立即删除,需自行承担相应责任。涉及到版权或其他问题,请及时联系我们删除处理邮件:weilaitui@qq.com。
     

    Copyright©2015-2025 粤公网安备 44030702000869号

    粤ICP备16078936号

    微信

    关注
    微信

    微信二维码

    WAP二维码

    客服

    联系
    客服

    联系客服:

    24在线QQ: 770665880

    客服电话: 020-82301567

    E_mail邮箱: weilaitui@qq.com

    微信公众号: weishitui

    韩瑞 小英 张泽

    工作时间:

    周一至周五: 08:00 - 24:00

    反馈

    用户
    反馈