zoukankan      html  css  js  c++  java
  • 5.Scala-匹配模式

    第5章 模式匹配

    5.1 switch

    与 default 等效的是捕获所有的 case_ 模式。如果没有模式匹配,抛出
    MatchError,每个 case 中,不用 break 语句。

    和 if 一样,match 也会返回值:
    你可以在 match 中使用任何类型,而不仅仅是数字。 
     
     
    笔记:
    def match1() = {
      var result = 0
      val op : Char = '-'
    
      op match {
        case '+' => result = 1
        case '-' => result = -1
        case _ => result = 0
      }
    
      println(result) //-1
    }
    
    match1()

    5.2 守卫

    像 if 表达式一样,match 也提供守卫功能,守卫可以是任何 Boolean 条件:

    笔记:

    def match2() = {
      for(c <- "+-*/123"){
        c match {
          case _ if Character.isDigit(c) => println("这是一个数字" + c)
          case '+' => println("字符为+号")
          case '-' => println("字符为-号")
          case '*' => println("字符为*号")
          case '/' => println("字符为/号")
          case _ => println("通配")
        }
      }
    }
    
    match2()

    5.3 模式中的变量

    如果 case 关键字后面跟着一个变量名,那么匹配的表达式会被赋值给那个变量。 
    val str = "+-3!" 
    for (i <- str.indices) {
        var sign = 0
        var digit = 0
        str(i) match {
          case '+' => sign = 1
          case '-' => sign = -1
          case ch if Character.isDigit(ch) => digit = Character.digit(ch, 10)
          case _ => 
        }
    println(str(i) + " " + sign + " " + digit)
    }
    Scala 中变量必须以小写字母开头,常量用大写字母,如果常量用小写字
    母开头需要加反引号。 

    5.4 类型模式

     可以匹配对象的任意类型,但是不直接匹配泛型类型,这样描述比较抽象,看下面的例子:

    这样做的意义在于,避免了使用 isInstanceOf 和 asInstanceOf 方法。

     

     笔记:

    //类型模式
    def match3() = {
      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 == 8) Array("aa")
      else if(a == 7) Array("aa", 1)
    
      val r1 = obj match {
        case i : Int => i
        case s : String => s
        case bi : BigInt => bi
        case m1 : Map[String, Int] => println("Map[String, Int]")
        case m2 : Map[Int, String] => m2
        case a1 : Array[Int] => a1 
        case a3 : Array[String] => a3
        case a2 : Array[any] => a2
      }
    
      println(r1 + ":" + r1.getClass.getName)
    
    }
    
    match3()

    5.5 匹配数组、列表、元组

    Array(0) 匹配只有一个元素且为 0 的数组。
    Array(x,y) 匹配数组有两个元素,并将两个元素赋值为 x 和 y。
    Array(0,_*) 匹配数组以 0 开始。
    1)匹配数组:
    for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0))) {
        val result = arr match {
            case Array(0) => "0" 
            case Array(x, y) => x + " " + y
            case Array(0, _*) => "0 ..." 
            case _ => "something else"
        }
        println(result)
    }

    2)匹配列表

    同样的方式可以应用于列表

     3)匹配元组

    集合元素通过匹配绑定到变量,这样的操作叫做“析构”。

    笔记:

    //匹配数组
    def match4() = {
    
      for(arr <- Array(Array(0), Array(1, 0), Array(0, 1, 1), Array(1, 1, 0), Array(1, 1, 1, 0))){
        arr match {
          case Array(0) => println("Array(0)")
          case Array(x, y) => println("Array(x, y)" + ":" + x + "," + y)
          case Array(x, y, z) => println("Array(x, y, z)" + ":" + x + "," + y + "," + z)
          case Array(1, arr @ _*) => println("Array(1, arr @ _*)" + ":" + arr.length)
          case _ => println("通配")
        }
      }
    }
    
    match4()

    5.6 提取器

    模式匹配,什么才算是匹配呢?即,case 中 unapply 方法返回 some 集合则为匹配成功,

    返回 none 集合则为匹配失败。下面看几个例子:

    1)unapply

    -- 调用 unapply,传入 unmber

    -- 接收返回值并判断返回值是None,还是 Some

    -- 如果是 Some,则将其解开,并将其中的值赋值给 n(就是 case Square(n) 中的 n)

    创建 object Square:
    object Square{
        def unapply(z: Double): Option[Double] = Some(math.sqrt(z))
    }
    
    模式匹配使用:
    val number: Double = 36.0
    
    number match {
        case Square(n) => println(s"square root of $number is $n")
        case _ => prinpln("nothing matched")
    }

     

      到底什么时候用 unapply 什么时候用 unapplySeq,要看参数个数。
    注意:如果要提取单个值,则应该返回一个目标类型的 Option,例如
    Option[Int],而不是 Option[(Int)];无参数的提取器可以用于 boolean 检查。

     

    5.7 变量声明中的模式

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

    val (x, y) = (1, 2)
    val (q, r)
    = BigInt(10) /% 3
    val arr = Array(1, 7, 2, 9)
    val Array(first, second, _
    *) = arr
    println(first, second)

    5.8 for表达式中的模式

    在 for 表达式中使用提取器: 
    
    
    import scala.collection.JavaConverters._

    for
    ((k, v) <- System.getProperties.asScala) println(k + " -> " + v) for ((k, "") <- System.getProperties.asScala) println(k) for ((k, v) <- System.getProperties.asScala if v == "") println(k)

     笔记:

    //for 循环中的模式匹配
    import scala.collection.JavaConverters._
    
    def match6() = {
      
      for ((k, v) <- System.getProperties.asScala)
        println(k + " -> " + v)
      for ((k, "") <- System.getProperties.asScala)
        println(k)
      for ((k, v) <- System.getProperties.asScala if v == "")
        println(k)
      
      
    }
    match6()

    5.9 样例类

    样例类首先是类,除此之外它是为模式匹配而优化的类,样例类用 case关键字进行声明: 

    1)样例类的创建

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

    2)当我们有一个类型为 Amount 的对象时,我们可以用模式匹配来匹配他

    的类型,并将属性值绑定到变量: 
    for (amt <- Array(Dollar(1000.0), Currency(1000.0, "EUR"), Nothing)) {
        val result = amt match {
            case Dollar(v) => "$" + v
            case Currency(_, u) => "Oh noes, I got " + u
            case Nothing => "" 
    }
        // Note that amt is printed nicely, thanks to the generated toString
        println(amt + ": " + result)
    }

    笔记:

    //样例类
    def match7() = {
      for (e <- Array(Dollar(1000.0), Currency(1000, "EUR"))) {
        e match {
          case Dollar(v) => println(v)
          case Currency(k, v) => println(k + "," + v)
        }
      }
    }
    
    match7()

    5.10 Copy方法和带名参数

    copy 创建一个与现有对象值相同的新对象,并可以通过带名参数来修改某些属性。 
    val amt = Currency(29.95, "EUR")
    val price = amt.copy(value = 19.95)
    println(amt) //Currency(29.95,EUR) println(price)  //Currency(19.95,EUR) println(amt.copy(unit
    = "CHF")) //Currency(29.95,CHF)

    5.11 Case语句的中置(缀)表达式

    什么是中置表达式?1 + 2,这就是一个中置表达式。如果 unapply 方法产出一个元组,你可以在 case 语句中使用中置表示
    法。比如可以匹配一个 List 序列,可以如下表示: 
    List(1, 7, 2, 9) match { 
      case first :: second :: rest => first + second + rest.length 
      case _ => 0
    }

    笔记:

    //Case语句的中置(缀)表达式
    def match8() = {
      val list = List(1, 2, 3, 4, 5, 6)
      list match {
        case l1 :: l2 :: l3 => println(l1 + "," + l2 + "," + l3)
      }
    }
    
    match8() //1,2,List(3, 4, 5, 6)

    5.12 匹配嵌套结构

    样例类经常被用于嵌套结构。例如,某个商店售卖的物品,有时,会将
    多个物品一起打着出售,我们有以下抽象:
     
    1)创建样例类
    abstract class Item
    case class Article(description: String, price: Double) extends Item
    case class Bundle(description: String, discount: Double, items: Item*) extends Item

    2)匹配嵌套结构

    val special = Bundle("Father's day special", 20.0,
      Article("Scala for the Impatient", 39.95),
      Bundle("Anchor Distillery Sampler", 10.0,
        Article("Old Potrero Straight Rye Whiskey", 79.95),
        Article("Junípero Gin", 32.95)))

    3)将 descr 绑定到第一个 Article 的描述

    val result1 = special match {
      case Bundle(_, _, Article(descr, _), _*) => descr
    }

    println(result1)

    4)通过@表示法将嵌套的值绑定到变量。_*绑定剩余 Item 到 rest

    val result2 = special match { 
      case Bundle(_, _, art @ Article(_, _), rest @ _*) => (art, rest)
    }
    println(result2)

    5)不使用 _* 绑定剩余 Item 到 rest

    val result3 = special match {
      case Bundle(_, _, art @ Article(_, _), rest) => (art, rest)
    }

    println(result3)

     6)计算某个 Item 价格的函数,并调用

    def price(it: Item): Double = {
      it match {
        case Article(_, p) => p
        case Bundle(_, disc, its @ _*) => its.map(price _).sum - disc
      }
    }
    price(special)

    笔记:

    //嵌套类的匹配
    def match9() = {
      val sale = Bundle("秘籍", 10,
        Article("九阳神功", 40),
        Bundle("系列",20,
          Article("Java系列", 80),
          Article("小说系列", 30)))
    
      val result1 = sale match {
        case Bundle(_, _, Article(descr, _), temp @ _*) => descr + "," + temp
      }
    
      println(result1) //九阳神功,WrappedArray(Bundle(系列,20.0,WrappedArray(Article(Java系列,80.0), Article(小说系列,30.0))))
    
      val result2 = sale match {
        case Bundle(_, _, art @ Article(_, _), rest @ _*) => (art, rest)
      }
      println(result2) //(Article(九阳神功,40.0),WrappedArray(Bundle(系列,20.0,WrappedArray(Article(Java系列,80.0), Article(小说系列,30.0)))))
    
      val result3 = sale match {
        case Bundle(_, _, art @ Article(_, _), rest) => (art, rest)
      }
      println(result3) //(Article(九阳神功,40.0),Bundle(系列,20.0,WrappedArray(Article(Java系列,80.0), Article(小说系列,30.0))))
    
    
      def price(it: Item): Double = {
        it match {
          case Article(_, p) => p
          case Bundle(_, disc, its @ _*) => its.map(price(_)).sum - disc
        }
      }
    
      println(price(sale))
    
    }
    
    
    match9()

    5.13 密封类

    如果想让 case 类的所有子类都必须在申明该类的相同的文件中定义,可
    以将样例类的通用超类声明为 sealed,叫做密封类,密封就是外部用户不能在
    其他文件中定义子类。
    sealed abstract class TrafficLightColor
      case object Red extends TrafficLightColor
      case object Yellow extends TrafficLightColor
      case object Green extends TrafficLightColor

    5.14 模拟枚举 

    for (color <- Array(Red, Yellow, Green))
      println(
        color match {
        case Red => "stop" 
        case Yellow => "hurry up" 
        case Green => "go" 
        }
      )

    笔记:

    //模拟枚举
    def match10() : Unit = {
    
      for (color <- Array(Red, Yellow, Green))
        println(
          color match {
            case Red => "stop"
            case Yellow => "hurry up"
            case Green => "go"
          }
        )
    }
    match10()

    5.15 偏函数 

    偏函数,它只对会作用于指定类型的参数或指定范围值的参数实施计算

    val f: PartialFunction[Char, Int] = {
      case '+' => 1
      case '-' => -1
    }
    
    println(f('-')) //-1
    println(f('+')) //1
    println(f.isDefinedAt('0')) //false

    map 和 collect:

        //额外讨论
        val list = List(1, 2, 3, 4, 5)
        val result1 = list.map(_ * 2)
        println(result1 + ", result1") //List(2, 4, 6, 8, 10)
    
        val result2 = list.collect{case x => x * 2}
        println(result2 + ", result2") //List(2, 4, 6, 8, 10)
    
    //    val result3 = List(1, 2, 3, 4, 5, "haha").map{case i: Int => i * 2}
    //    println(result3 + ", result3") //scala.MatchError
    
        val result4 = List(1, 2, 3, 4, 5, "haha").collect{case i: Int => i * 2}
        println(result4 + ", result2") //List(2, 4, 6, 8, 10)

    再深入探讨一点点:

    我们定义一个将 List 集合里面数据 +1 的偏函数

    val f1 = new PartialFunction[Any, Int] {
      def apply(any: Any) = any.asInstanceOf[Int] + 1
    
      def isDefinedAt(any: Any) = if (any.isInstanceOf[Int]) true else false
    }
    val rf1 = List(1, 3, 5, "seven") collect f1 //collect 会调用 isDefinedAt 函数再执行 apply 方法,而 map 会直接执行 apply
    println(rf1) //List(2, 4, 6)

      

    如上功能,等同于:

    def f2: PartialFunction[Any, Int] = {
      case i : Int => i + 1
    }
    val rf2 = List(1, 3, 5, "seven") collect(f2)
    println(rf2) //List(2, 4, 6)
  • 相关阅读:
    iOS面试题6.30总结
    关于外挂
    webstorm快捷方式
    HTML注释的一些规范
    认识Python
    正体复本术解决容易疲劳、不能持续集中精力工作-海淀区非物质文化遗产:#正体复本术#
    皮肤发痒的观察与思考
    win10不错的快捷键
    项目属性的target platform和target platform version到底是什么(vs2015开发windows驱动小记)
    玩Web虎-运行时受保护文件不可复制
  • 原文地址:https://www.cnblogs.com/LXL616/p/11117143.html
Copyright © 2011-2022 走看看