九、模式匹配

目录

 

一、match

1、match

2、守卫

3、模式中的变量

4、类型匹配

5、匹配数组

6、匹配列表

7、匹配元组

8、对象匹配

二、变量声明中的模式

三、for表达式中的模式

四、样例类

五、case语句的中置(缀)表达式

六、匹配嵌套结构

七、密封类


一、match

1、match

(1)基本介绍

Scala中的模式匹配类似于Java中的switch语法,但是更加强大。

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

(2)Java Switch

switch(expression){
    case value :
       //语句
       break; //可选
    case value :
       //语句
       break; //可选
    //你可以有任意数量的case语句
    default : //可选
       //语句
}

(3)Scala的模式匹配

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

(4)Scala match的细节和注意事项

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

  • 如果所有case都不匹配,又没有写case _ 分支,那么会抛出MatchError

  • 每个case中,不用break语句,自动中断case

  • 可以在match中使用其它类型,而不 仅仅是字符

  • => 等价于 java swtich 的 :

  • => 后面的代码块到下一个 case, 是作为一个整体执行,可以使用{} 扩起来,也可以不扩。

2、守卫

(1)基本介绍

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

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

3、模式中的变量

(1)基本介绍

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

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

4、类型匹配

(1)基本介绍

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

    val a = 6
    val obj = if(a == 1) 1
    else if(a == 2) "2"
    else if(a == 3) BigInt(3)
    else if(a == 4) Map("aa" -> 1)
    else if(a == 5) Map(1 -> "aa")
    else if(a == 6) Array(1, 2, 3)
    else if(a == 7) Array("aa", 1)
    else if(a == 8) Array("aa")
    val result = obj match {
      case a : Int => a
      case b : Map[String, Int] => "对象是一个字符串-数字的Map集合"
      case c : Map[Int, String] => "对象是一个数字-字符串的Map集合"
      case d : Array[String] => "对象是一个字符串数组"
      case e : Array[Int] => "对象是一个数字数组"
      case f : BigInt => Int.MaxValue
      case _ => "啥也不是"
    }

(2)类型匹配注意事项

  • Map[String, Int] 和Map[Int, String]是两种不同的类型,其它类推。

  • 在进行类型匹配时,编译器会预先检测是否有可能的匹配,如果没有则报错.

val obj = 10
val result = obj match {
case a : Int => a
case b : Map[String, Int] => "Map集合"
case _ => "啥也不是"
}
  • 一个说明:
    val obj = 1
    val result11= obj match {
      case i : Int => i
    }//case i : Int => i 表示 将 i = obj (其它类推),然后再判断类型
  • 如果 case _ 出现在match 中间,则表示隐藏变量名,即不使用,而不是表示默认匹配。

    var obj = if(a == 1) 1
    else if(a == 2) "2"
    else if(a == 3) BigInt(3)
    else if(a == 4) Map("aa" -> 1)
    else if(a == 5) Map(1 -> "aa")
    else if(a == 6) Array(1, 2, 3)
    else if(a == 7) Array("aa", 1)
    else if(a == 8) Array("aa")
    val result1 = obj match {
      case a : Int => a
      case _ : BigInt => Int.MaxValue //看这里!
      case b : Map[String, Int] => "对象是一个字符串-数字的Map集合"
      case c : Map[Int, String] => "对象是一个数字-字符串的Map集合"
      case d : Array[String] => "对象是一个字符串数组"
      case e : Array[Int] => "对象是一个数字数组"
      case _ => "啥也不是"
    }

5、匹配数组

(1)基本介绍

  • Array(0) 匹配只有一个元素且为0的数组。

  • Array(x,y) 匹配数组有两个元素,并将两个元素赋值为x和y。当然可以依次类推Array(x,y,z) 匹配数组有3个元素的等等....

  • Array(0,_*) 匹配数组以0开始

    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 + "=" + y
        case Array(0, _*) => "以0开头和数组"
        case _ => "什么集合都不是"
      }
      println("result = " + result)
    }

6、匹配列表

    for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0))) {
      val result = list match {
        case 0 :: Nil => "0" //case List(0) => "0"
        case x :: y :: Nil => x + " " + y //
        //tail 返回一个列表,包含除了第一元素之外的其他元素,此处即匹配第一个元素为0的列表
        case 0 :: tail => "0 ..."
        case _ => "something else"
      }
      println(result)
    }

7、匹配元组

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

8、对象匹配

(1)基本介绍

  • 对象匹配,什么才算是匹配呢?,规则如下:

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

  • 返回none集合则为匹配失败

(2)案例1

object CollectionTest {
  def main(args: Array[String]): Unit = {
    // 模式匹配使用:
    val number: Double = 36.0
    number match {
      case Square(n) => println(n)
      case _ => println("nothing matched")
    }

  }
}
object Square {
  def unapply(z: Double): Option[Double] = Some(math.sqrt(z))
  def apply(z: Double): Double = z * z
}

案例 1注意点:

  • 构建对象时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集合则为匹配失败

(3)案例2

object CollectionTest {
  def main(args: Array[String]): Unit = {
    val namesString = "Alice,Bob,Thomas"
    //说明
    namesString match {
      case Names(first, second, third) => {
        println("the string contains three people's names")
        // 打印字符串
        println(s"$first $second $third")
      }
      case _ => println("nothing matched")
    }
  }
}
object Names {
  def unapplySeq(str: String): Option[Seq[String]] = {
    if (str.contains(",")) Some(str.split(","))
    else None
  }
}

案例 2 注意点:

  • 当case 后面的对象提取器方法的参数为多个,则会默认调用def unapplySeq() 方法

  • 如果unapplySeq返回是Some,获取其中的值,判断得到的sequence中的元素的个数是否是三个,如果是三个,则把三个元素分别取出,赋值给first,second和third

  • 其它的规则不变.

二、变量声明中的模式

match中每一个case都可以单独提取出来,意思是一样的.

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

三、for表达式中的模式

    val map = Map("A"->1, "B"->0, "C"->3)
    for ( (k, v) <- map ) {
      println(k + " -> " + v)
    }
    //说明
    for ((k, 0) <- map) {
      println(k + " --> " + 0)
    }
    //说明
    for ((k, v) <- map if v == 0) {
      println(k + " ---> " + v)
    }

四、样例类

1、基本介绍

  • 样例类仍然是类

  • 样例类用case关键字进行声明

  • 样例类是为模式匹配而优化的类

  • 构造器中的每一个参数都成为val——除非它被显式地声明为var(不建议这样做)

  • 在样例类对应的伴生对象中提供apply方法让你不用new关键字就能构造出相应的对象

  • 提供unapply方法让模式匹配可以工作

  • 自动生成toString、equals、hashCode和copy方法(有点类似模板类,直接给生成,供程序员使用)

  • 除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们

  abstract class Amount
  case class Dollar(value: Double) extends Amount
  case class Currency(value: Double, unit: String) extends Amount
  case object NoAmount extends Amount

2、实例 1

一个类型为Amount的对象,可以用模式匹配来匹配他的类型,并将属性值绑定到变量(即:把样例类对象的属性值提取到某个变量)

object CollectionTest {
  abstract class Amount
  case class Dollar(value: Double) extends Amount
  case class Currency(value: Double, unit: String) extends Amount
  case object NoAmount extends Amount
  def main(args: Array[String]): Unit = {
    for (amt <- Array(Dollar(1000.0), Currency(1000.0, "RMB"), NoAmount)) {
      val result = amt match {
        //说明
        case Dollar(v) => "$" + v
        //说明
        case Currency(v, u) => v + " " + u
        case NoAmount => ""
      }
      println(amt + ": " + result)
    }
  }
}

3、实例 2

样例类的copy方法和带名参数

copy创建一个与现有对象值相同的新对象,并可以通过带名参数来修改某些属性。

object CollectionTest {
  abstract class Amount
  case class Dollar(value: Double) extends Amount
  case class Currency(value: Double, unit: String) extends Amount
  case object NoAmount extends Amount
  def main(args: Array[String]): Unit = {
    val amt = Currency(29.95, "RMB")
    val amt1 = amt.copy() //创建了一个新的对象,但是属性值一样
    val amt2 = amt.copy(value = 19.95) //创建了一个新对象,但是修改了货币值
    val amt3 = amt.copy(unit = "英镑")//创建了一个新对象,但是修改了货币单位
    println(amt)
    println(amt2)
    println(amt3)
  }
}

五、case语句的中置(缀)表达式

什么是中置表达式?1 + 2,这就是一个中置表达式。如果unapply方法产出一个元组,你可以在case语句中使用中置表示法。比如可以匹配一个List序列

object CollectionTest {
  def main(args: Array[String]): Unit = {
    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("匹配不到...")
    }
  }
}

六、匹配嵌套结构

1、案例商品捆绑打折出售

现在有一些商品,请使用Scala设计相关的样例类,完成商品捆绑打折出售。要求

  • 商品捆绑可以是单个商品,也可以是多个商品。

  • 打折时按照折扣x元进行设计.

  • 能够统计出所有捆绑商品打折后的最终价格

(1)注意点 1:将 description 绑定到第一个Book的描述

取出 val sale = Bundle("书籍", 10,  Book("漫画", 40), Bundle("文学作品", 20, Book("《阳关》", 80), Book("《围城》", 30)))
这个嵌套结构中的 "漫画"

object CollectionTest {
  abstract class Item // 项
  case class Book(description: String, price: Double) extends Item
  //Bundle 捆 , discount: Double 折扣 , item: Item* :表示可变参数,可以传无数多个item
  case class Bundle(description: String, discount: Double, item: Item*) extends Item
  def main(args: Array[String]): Unit = {
    //给出案例表示有一捆数,单本漫画(40-10) +文学作品(两本书)(80+30-20) = 30 + 90 = 120.0
    val sale = Bundle("书籍", 10,  Book("漫画", 40), Bundle("文学作品", 
        20, Book("《阳关》", 80), Book("《围城》", 30)))
    val res = sale match  {
      //如果我们进行对象匹配时,不想接受某些值,则使用_ 忽略即可,_* 表示所有
      case Bundle(_, _, Book(desc, _), _*) => desc
    }
    println(res)
  }
}

(2)注意点 2:通过@表示法将嵌套的值绑定到变量。_*绑定剩余Item到rest

将 "漫画" 和

val sale = Bundle("书籍", 10,  Book("漫画", 40), 
Bundle("文学作品", 20, Book("《阳关》", 80), Book("《围城》", 30)))

这个嵌套结构中的 "漫画" 和 紫色的部分 绑定到变量,即赋值到变量中。

object CollectionTest {
  abstract class Item // 项
  case class Book(description: String, price: Double) extends Item
  //Bundle 捆 , discount: Double 折扣 , item: Item* :表示可变参数,可以传无数多个item
  case class Bundle(description: String, discount: Double, item: Item*) extends Item
  def main(args: Array[String]): Unit = {
    //给出案例表示有一捆数,单本漫画(40-10) +文学作品(两本书)(80+30-20) = 30 + 90 = 120.0
    val sale = Bundle("书籍", 10,  Book("漫画", 40), Bundle("文学作品", 20,
         Book("《阳关》", 80), Book("《围城》", 30)))
    val result2 = sale match {
      case Bundle(_, _, art @ Book(_, _), rest @ _*) => (art, rest)
    }
    println("result2 = " + result2)
    println("art = " + result2._1)
    println("rest = " + result2._2)
  }
}

(3)注意点 3:不使用_*绑定剩余Item到rest

object CollectionTest {
  abstract class Item // 项
  case class Book(description: String, price: Double) extends Item
  //Bundle 捆 , discount: Double 折扣 , item: Item* :表示可变参数,可以传无数多个item
  case class Bundle(description: String, discount: Double, item: Item*) extends Item
  def main(args: Array[String]): Unit = {
    //给出案例表示有一捆数,单本漫画(40-10) +文学作品(两本书)(80+30-20) = 30 + 90 = 120.0
    val sale = Bundle("书籍", 10,  Book("漫画", 40), Bundle("文学作品", 20,
         Book("《阳关》", 80), Book("《围城》", 30)))
    val result2 = sale match {
      //说明因为没有使用 _* 即明确说明没有多个Bundle,所以返回的rest,就不是WrappedArray了。
      case Bundle(_, _, art @ Book(_, _), rest) => (art, rest)
    }
    println("result2 = " + result2)
    println("art =" + result2._1)
    println("rest=" + result2._2)
  }
}

2、求解:包含递归操作

object CollectionTest {
  abstract class Item // 项
  case class Book(description: String, price: Double) extends Item
  //Bundle 捆 , discount: Double 折扣 , item: Item* :表示可变参数,可以传无数多个item
  case class Bundle(description: String, discount: Double, item: Item*) extends Item
  def main(args: Array[String]): Unit = {
    //给出案例表示有一捆数,单本漫画(40-10) +文学作品(两本书)(80+30-20) = 30 + 90 = 120.0
    val sale = Bundle("书籍", 10,  Book("漫画", 40), Bundle("文学作品", 20,
         Book("《阳关》", 80), Book("《围城》", 30)))
    val money = price(sale)
    println(money)//120.0
  }
  def price(it: Item): Double = {
    it match {
      case Book(_, p) => p
      //生成一个新的集合,_是将its中每个循环的元素传递到price中it中。递归操作,分析一个简单的流程
      case Bundle(_, disc, its @ _*) => its.map(price _).sum - disc
    }
  }
}

七、密封类

如果想让case类的所有子类都必须在申明该类的相同的源文件中定义,可以将样例类的通用超类声明为sealed,这个超类称之为密封类。

密封就是不能在其他文件中定义子类。

 

BF算法是一种普通的模式匹配算法,其思想是将目标串S的第一个字符与模式串P的第一个字符进行匹配,若相等,则继续比较S的第二个字符和P的第二个字符;若不相等,则比较S的第二个字符和P的第一个字符,依次比较下去,直到得出最后的匹配结果。BF算法匹配的步骤如下: 假设目标串S长度为n,模式串P长度为m。 1. 从目标串S的第一个字符开始,依次与模式串P的第一个字符、第二个字符、第三个字符……第m个字符进行比较,若相等,则继续比较下一个字符;若不相等,则从目标串S的下一个字符开始,再次与模式串P的第一个字符进行比较。 2. 如果在比较过程中,发现目标串S的某个字符与模式串P的某个字符不相等,则需要回溯到上一次比较的位置,重新开始匹配。具体来说,假设在比较S[i]和P[j]时发现不相等,那么需要将i和j分别回溯到i-j+2和1的位置,然后重新开始匹配。 3. 如果在比较过程中,发现模式串P已经匹配完了所有字符,则说明匹配成功,返回目标串S中匹配成功的第一个字符的位置。 4. 如果在比较过程中,发现目标串S已经没有足够的字符可以与模式串P进行比较,则说明匹配失败,返回-1。 下面是一个示例,假设目标串S为"ababcababa",模式串P为"aba",则BF算法匹配的步骤如下: 第一趟:ababcababa 第二趟:ababcababa 第三趟:ababcababa 第四趟:ababcababa 第五趟:ababcababa 第六趟:ababcababa 第七趟:ababcababa 第八趟:ababcababa 第趟:ababcababa 匹配成功,返回目标串S中匹配成功的第一个字符的位置,即2。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值