古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。这篇文章主要讲述Scala 模式匹配详解相关的知识,希望能为你提供帮助。
【Scala 模式匹配详解】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 基本语法
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,23
3.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: 13
3.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))
3.7 样例类?val student = Student("法外狂徒,张三",11)?
?,该语句在执行时,实际调用的是 Student 伴生对象中的apply 方法,因此不用 new 关键字就能构造出相应的对象。?Student("法外狂徒,张三", 11)?
?写在 case 后时??[case Student("法外狂徒,张三", 11) =>
"法外狂徒,张三, 18"]?
?,会默认调用 unapply 方法(对象提取器),Student 作为 unapply 方法的参数,unapply 方法将 Student 对象的 name 和 age 属性提取出来,与 ??Student("法外狂徒,张三", 11)?
?中的属性值进行匹配?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)
查看字节码文件:
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 finalMODULE$;
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)
推荐阅读
- Ubuntu下开启/关闭防火墙及端口#私藏项目实操分享#
- zabbix-nginx监测及自定义模板
- #yyds干货盘点#Linux用户和组
- #yyds干货盘点#Centos7更换阿里YUM源
- 推荐几款大家常使用的 SSH 客户端工具
- ubuntu18.04升级kennel 5.10失败记录 #yyds干货盘点#
- #yyds干货盘点#Linux简介
- dgango中admin下添加搜索功能
- #yyds干货盘点#Linux进程