大数据技术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]]
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 AbstractFunction2implements 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 > 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="060105....") public class User implements Product, Serializable { private final String name; private final int age; public static Option4 变量声明中的模式匹配> 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 , User> tupled() { return User..MODULE$.tupled(); } public static Function1 > 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); } }
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) }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)