ScalaNote21-模式匹配

网友投稿 548 2022-09-02

ScalaNote21-模式匹配

ScalaNote21-模式匹配

match

val oper = '#'val n1 = 20val n2 = 10var res = 0oper match {case '+' => res = n1 + n2case '-' => res = n1 - n2case '*' => res = n1 * n2case '/' => res = n1 / n2case _ => println("oper error")}println("res=" + res)

oper errorres=0oper: Char = #n1: Int = 20n2: Int = 10res: Int = 0

如果所有case都不匹配,那么会执行​​case _​​分支如果所有case都不匹配,又没有写case _ 分支,那么会抛出MatchError每个case中,不用break语句,自动中断case=> 后面的代码块到下一个 case, 是作为一个整体执行,可以使用{} 扩起来,也可以不扩

守卫

如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫! 看个Demo:

for (ch <- "+-3!") {var sign = 0var digit = 0ch match {case '+' => sign = 1case '-' => sign = -1// 说明..case _ if ch.toString.equals("3") => digit = 3case _ => sign = 2}println(ch + " " + sign + " " + digit)}

+ 1 0- -1 03 0 3! 2 0

代码结构和match基本一致_ if这个操作暂且只能记住了,scala中"_"意义真的多,import时还能指代引入所有函数。。

模式中的变量

如果在case关键字后跟变量名,那么match前表达式的值会赋给那个变量!

val ch = "V"ch match {case "+" => println("ok")case mychar => println("mychar = "+mychar)// case _ => println("ok")}

mychar = Vch: String = V

mychar这里的逻辑是把ch赋值给mycharmatch表达式本身可以赋给一个变量

val ch = "V"var ch1= ch match {case "+" => println("ok")case mychar => mychar// case _ => println("ok")}

ch: String = Vch1: Any = V

类型匹配

可以匹配对象的任意类型,这样做避免了使用isInstanceOf和asInstanceOf方法。看个Demo:

val a = 2val obj = a match { case 1=>1 case 2 => "2" case 3 => Map("aa" -> 1) case _ => "Nothing"}val result = obj match { case a : Int => a case a : String => a+" is String" case a : Array[String] => a+"is Array" case _:Float => "is Float" case _ => "Nothing too"}println(result)

2 is Stringa: Int = 2obj: Any = 2result: Any = 2 is String

​​case a:Int​​,这里相当于把obj赋值给a​​case _:Float​​,这里相当于直接验证obj是否为Float类型

匹配数组

直接把韩老师的代码搬过来了:

for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0),Array(1, 1, 0), Array(1, 1, 0, 1))) {val result = arr match {case Array(0) => "0"case Array(x, y) => x + "=" + ycase Array(0, _*) => "0^*"case _ => "Nothing"}println("result = " + result)}

result = 0result = 1=0result = 0^*result = Nothingresult = Nothing

Array(0) 匹配只有一个元素且为0的数组Array(x,y) 匹配数组有两个元素,并将两个元素赋值为x和y。当然可以依次类推Array(x,y,z) 匹配数组有3个元素的等等…Array(0,_*) 匹配数组以0开始

匹配列表

感觉有点正则的意思了,不过scala这些用法真多,不能搞点通用的方法吗?

for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0))) {val result = list match {case 0 :: Nil => "0" //只有一个元素0case x :: y :: Nil => x + " " + y //有两个元素case 0 :: tail1111 => "0 ..." //0开头的元素,tail可以为任务字符串case _ => "something else"}println(result)}

01 00 ...something else

匹配元组

for (pair <- Array((0, 1), (1, 0), (1, 1),(1,0,2),(10,30))) {val result = pair match { case (0, _) => "0 ..." //第一元素为0,一共两个元素case (y, 0) => y // 第二个元素为0case (10,30)=>(10,30) case _ => "other" //}println(result)}

0 ...1otherother(10,30)

对象匹配

这里看的比较蒙,涉及到前面讲的单例对象。在伴生对象中,我们定义apply方法,可以不通过new直接新建一个实例。但是单例对象似乎也可以。下面的单例对象提供了apply方法和unapply方法。这俩似乎不专属的函数??先看个用法吧~ }

object Square { //说明 //1. unapply方法是对象提取器 //2. 接收z:Double 类型 //3. 返回类型是Option[Double] //4. 返回的值是 Some(math.sqrt(z)) 返回z的开平方的值,并放入到Some(x)def unapply(z: Double): Option[Double] = Some(math.sqrt(z)) // apply是对象生成器??def apply(z: Double): Double = z * z}println("Square(5) = " + Square(5))println("Square.apply(5) = " + Square.apply(5))println("Square.unapply(25) = " + Square.unapply(25))

Square(5) = 25.0Square.apply(5) = 25.0Square.unapply(25) = Some(5.0)defined object Square

这样看单例对象和伴生类型的apply方法一样? 再说回对象匹配:

case中对象的unapply方法(对象提取器)返回Some集合则为匹配成功返回none集合则为匹配失败

val number: Double = 36.0number match {case Square(n) => println(n)case _ => println("nothing matched")}

6.0number: Double = 36.0

构建对象时apply会被调用 ,比如 val n1 = Square(5)当将 Square(n) 写在 case 后时[case Square(n) => xxx],会默认调用unapply 方法(对象提取器)number 会被 传递给def unapply(z: Double) 的 z 形参如果返回的是Some集合,则unapply提取器返回的结果会返回给 n 这个形参case中对象的unapply方法(提取器)返回some集合则为匹配成功返回None集合则为匹配失败

再看另一个case

object Names { //unapplySeq是一个专有名词,指定的函数def unapplySeq(str: String): Option[Seq[String]] = {if (str.contains(",")) Some(str.split(","))else None}}val namesString = "Alice,Bob,Thomas"//说明namesString match { //first, second, third这三个参数名随便取,必须要要是三个case Names(first, second, third) => {println("the string contains three people's names")// 打印字符串println(s"$first $second $third")}case _ => println("nothing matched")}

the string contains three people's namesAlice Bob Thomasdefined object NamesnamesString: String = Alice,Bob,Thomas

当执行​​case Names(first, second, third)​​

会调用 unapplySeq(str),把 “Alice,Bob,Thomas” 传入给 str如果 返回的是 Some(“Alice”,“Bob”,“Thomas”),分别给(first, second, third)注意,这里的返回的值的个数需要和(first, second, third)要一样如果返回的None ,表示匹配失败

简略的做变量命名的方法,scala中骚操作是真的多。。。

val (x, y) = (1, 2)val (q, r) = BigInt(10) /% 3 //说明 q = BigInt(10) / 3 r = BigInt(10) % 3val arr = Array(1, 7, 2, 9)val Array(first, second, _*) = arr // 提出arr的前两个元素println(first, second)

(1,7)x: Int = 1y: Int = 2q: scala.math.BigInt = 3r: scala.math.BigInt = 1arr: Array[Int] = Array(1, 7, 2, 9)first: Int = 1second: Int = 7

for表达式中的模式

filter不是就能完成过滤操作了吗?这是为了一题多解?

val map = Map("A"->1, "B"->0, "C"->3)for ( (k, v) <- map ) {println(k + " -> " + v)}// 过滤出value=0,key-valuefor ((k, 0) <- map) {println(k + " --> " + 0)}// 和上面的功能一样for ((k, v) <- map if v == 0) {println(k + " ---> " + v)}

A -> 1B -> 0C -> 3B --> 0B ---> 0map: scala.collection.immutable.Map[String,Int] = Map(A -> 1, B -> 0, C -> 3)

样例类

样例类是干嘛滴?他的优点和用处我暂时还没有体会到~样例类的基本特点如下:

具体用法先看个demo,慢慢体会

abstract class Amountcase class Dollar(value: Double) extends Amount case class Currency(value: Double, unit: String) extends Amountcase object NoAmount extends Amount // Dollar、Currency、NoAmount为三个样例类 val Arr = Array(Dollar(1000.0), Currency(1000.0, "RMB"), NoAmount)for (amt <- Arr) {val result = amt match {//和前面对象匹配逻辑类似 // 如果是Dollar类,会调用unapply方法把amt赋值给了vcase Dollar(v) => "$" + v//说明case Currency(v, u) => v + " " + ucase NoAmount => ""}println(amt + ": " + result)}

Dollar(1000.0): $1000.0Currency(1000.0,RMB): 1000.0 RMBNoAmount: defined class Amountdefined class Dollardefined class Currencydefined object NoAmountArr: Array[Product with Serializable with Amount] = Array(Dollar(1000.0), Currency(1000.0,RMB), NoAmount)

刚才有提到样例类提供了unapply和apply等方法,也提供了copy方法,copy创建一个与现有对象值相同的新对象,并可以通过带名参数来修改某些属性!看个例子:

val amt = Currency(29.95, "RMB")val amt1 = amt.copy() //创建了一个新的对象,但是属性值一样val amt2 = amt.copy(value = 19.95) //创建了一个新对象,但是修改了货币单位val amt3 = amt.copy(unit = "Pound")//..println(amt)println(amt2)println(amt3)

Currency(29.95,RMB)Currency(19.95,RMB)Currency(29.95,Pound)amt: Currency = Currency(29.95,RMB)amt1: Currency = Currency(29.95,RMB)amt2: Currency = Currency(19.95,RMB)amt3: Currency = Currency(29.95,Pound)

中置(缀)表达式

还是没有体会到这个用法的用处。。。直接看例子,后面用到再细究吧

List(1, 3, 5, 9) match { //修改并测试//1.两个元素间::叫中置表达式,至少first,second两个匹配才行.//2.first 匹配第一个 second 匹配第二个, rest 匹配剩余部分(5,9)case first :: second :: rest => println(first + second + rest.length) //case _ => println("Nothing Match...")}

6

匹配嵌套结构

这个题目蛮有意思的,综合使用了直接介绍的样例类、对象匹配、递归等方法。先看题干: 现在有一些商品,请使用Scala设计相关的样例类,完成商品捆绑打折出售。要求

商品捆绑可以是单个商品,也可以是多个商品打折时按照折扣x元进行设计能够统计出所有捆绑商品打折后的最终价格

// 定义抽象类,可以直观的理解成他是商品粒度的,也是最细粒度的abstract class Item // 定义样例类Book,其继承Item类。可以直观理解为书本类别商品,参数为描述和价格case class Book(description: String, price: Double) extends Item//Bundle 捆 , discount: Double 折扣 , item: Item* , 注意这里的Item是可变参数,即可以传入多个参数,并且可以是Item的子类// 从逻辑上讲,discount指这些Item的总价在减去折扣case class Bundle(description: String, discount: Double, item: Item*) extends Item

defined class Itemdefined class Bookdefined class Bundle

下面定义一个Bundle,作为测试数据

sale变量为一个Bundle的实例参数description:book_info,discount:10,item有两个参数,一个是Book(“book01”, 40),一个是Bundle那么最终最终价格为(40-10)+(80+30-20)

val sale = Bundle("book_info", 10, Book("book01", 40), Bundle("literature", 20, Book("book02", 80), Book("book03", 30)))

sale: Bundle = Bundle(book_info,10.0,WrappedArray(Book(book01,40.0), Bundle(literature,20.0,WrappedArray(Book(book02,80.0), Book(book03,30.0)))))

那么我们的处理逻辑就是去除Item里的价格和折扣的价格

先取出折扣价格10,再取出book01的价格40,计算折扣之后的而价格对于第二个Item,是Bundle,这时候可以采取递归的逻辑,取出折扣价格和对应商品的原价

先看怎么取出对应的元素

下面的discount,description,price只是个代称,名字随便取不想接受某些值,则使用_ 忽略即可_* 表示所有

sale match{ case Bundle(_,discount,Book(description,price),_*)=>(discount,price)}

res21: (Double, Double) = (10.0,40.0)

如果取出整个变量咋取?

和上面取出元素逻辑一致,可以直接指代,比如​​case Bundle(_,_,book01,_*)=>book01​​​,这样就可以把​​Book("book01", 40)​​取出来那如果想把​​_*​​​取出来给一个变量,咋整?此时可用@符号,即​​rest @ _*​​

看个demo

val result2 = sale match { // art@_也可以case Bundle(_, _, art@Book(_, _), rest@_*) => (art, rest)}println(result2)println("art =" + result2._1)println("rest=" + result2._2)

(Book(book01,40.0),WrappedArray(Bundle(literature,20.0,WrappedArray(Book(book02,80.0), Book(book03,30.0)))))art =Book(book01,40.0)rest=WrappedArray(Bundle(literature,20.0,WrappedArray(Book(book02,80.0), Book(book03,30.0))))result2: (Book, Seq[Item]) = (Book(book01,40.0),WrappedArray(Bundle(literature,20.0,WrappedArray(Book(book02,80.0), Book(book03,30.0)))))

至此,我们对已知结构的数据可以完成数据取出,计算的操作了,那么怎么使之通用化呢?考虑递归的逻辑

用对象匹配的方法,取出对应商品的原价如果Item是Bundle,递归调用,取出里面的价格和折扣信息

直接看代码:

def price(it: Item): Unit = { it match { //如果是Book对象,则取出价格 case Book(index, p) => println(index+": "+p) //1、如果是Bundle,会取出所有的item即可变参数Item* //2、对所有的item进行遍历,遍历采用map方法 //3、map中递归调用price函数,目前来看就是提取book对象的价格元素 case Bundle(_, disc, its @ _*) => its.map(price) }}price(sale)

book01: 40.0book02: 80.0book03: 30.0price: (it: Item)Unit

原始价格我们都可以取出来,怎么计算折后价格呢?逻辑应该也比较清晰

把原始价格都加起来把折扣价格都加起来做加法即可

val sale = Bundle("book_info", 10, Book("book01", 40), Bundle("literature", 20, Book("book02", 80), Book("book03", 30)))

def price(it: Item): Double = { it match { //如果是Book对象,则取出价格 case Book(_, p) => {println(p);p} //1、如果是Bundle,会取出所有的item即可变参数Item* //2、对所有的item进行遍历,遍历采用map方法 //3、map中递归调用price函数 case Bundle(_, disc, its @ _*) => its.map(price).sum - disc }}// --- 递归之后的计算逻辑// 1)传入sale之后,走第二个case分支 // 2)取出disc和Item*// 3)对Item*进行遍历 //3.1 先取出book01的价格,此时价格应该返回到map之后的集合A中 //3.2 再调用price函数对第二个Item处理,此时也是先取出折扣价,再一次取出book02、book03的原价,放在map之后的集合B中 //3.3 对第二个Item返回的集合B求和,减去折扣价,这时候相当于计算好折后价,返回给集合A //3.4 最终集合A求和,这个时候第一个元素是原价,再减去折扣价就行// --- 加入println逻辑之后 // 1、最里面的Bundle,取出book02、book03的价格,求和-折扣,即80+30-20=90 // 2、之后90传给集合A,计算90+40-10price(sale)

40.080.030.0price: (it: Item)Doubleres41: Double = 120.0

密封类

2020-03-15 于南京市栖霞区

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:购物车增删改与清空,用Redis实现一下吧(购物车为什么用redis)
下一篇:ScalaNote22-函数式高级编程
相关文章

 发表评论

暂时没有评论,来抢沙发吧~