zoukankan      html  css  js  c++  java
  • Scala学习十四——模式匹配和样例类

    一.本章要点

    • match表达式是更好的switch,不会有意外调入下一个分支
    • 如果没有模式能够匹配,会抛出MatchError,可以用case _模式避免
    • 模式可以包含一个随意定义的条件,称做守卫
    • 你可以对表达式的类型进行匹配;优先选择模式匹配而不是isInstanceOf/asInstanceOf
    • 你可以匹配数组,元祖,样例类的模式,然后匹配到不同部分绑定到变量
    • 在for表达式中,不能呢匹配的情况会被安静的跳过
    • 样例类是编译器会为之自动

    二.更好的switch

      

    var sign=...
    val ch:Char=...
    ch match{
    case '+'=>sign=1
    case '-'=>sign=-1
    case _=>sign=0
    }
    //简写
    sign=ch match{
    ....
    }

    三.守卫

      添加守卫做筛选条件,例:

    ch match{
    case '+'=>sign=1
    case '-'=>sign=-1
    //筛选所有数字
    case _ if Charavter.isDigit(ch)=>digit=Character.digit(ch,10)
    case _=>sign=0
    }

    四.模式中的变量

      如果case关键字后面跟着一个变量名,那么匹配的表达式会被赋值给那个变量。例如:

    str(i) match{
    case '+'=>sign=1
    ...
    case ch=>Character.digt(ch,10)
    }

    五.类型模式

      对表达式的类型进行匹配,例:

    obj match{
    case x:Int=>x
    case s:String=>Integer.parseInt(s)
    case _:BigInt=>Int.MaxValue
    case _=>'0'
    }

      注:在Scala中,更倾向与使用这样的匹配模式,而不是isInstanceOf操作符;

        匹配时,必须给出一个变量名,否则会拿对象本身匹配;

        匹配发生在运行期,Java虚拟机的泛型是擦掉的,因此不能用类型匹配特定的Map类型  

    六.匹配数组、列表和元祖

      匹配数组:

    arr match{
    //通过这种绑定让你轻松的访问复杂结构各部份名:”析构“
    //匹配包含0的数组 case Array(0)=>"0" //匹配任何带有两个元素的数组,并将这两个元素分别绑定在x和y case Array(x,y)=>x+" "+y //匹配任何以0开始的数组 case Array(0,_*)=>"0..." case _=>"someting else" }

      使用List表达式或者::匹配列表:

    lst match{
    case 0::Nil=>"0"
    case x::y::Nil=>x+" "+y
    case 0::tail=>"0..."
    case _=>"something else"
    }

      匹配元组:

    pair match{
    case (0,)=>"0..."
    case (y,0)=>y+"0"
    case _=>"neither is 0"
    }

    七.提取器

      前面的匹配数组,列表,元组功能背后的机制:提取器机制——带有对象中提取值的unapply或unapplySeq方法的对象。unapply方法提取固定数量的对象,unapplySeq提取一个序列。

      如:

    arr match{
    case Array(0,x)=>...
    }

      Array伴生对象就是一个提取器——定义了一个unapplySeq方法,调用时是以被执行匹配动作的表达式作为参数,而不是模式上中看上去像是参数的表达式。unapplySeq(arr)产出一个序列的值,即数组的值。

      正则表达式是另一个适合用提取器的场景,注意这时的提取器不是伴生对象,而是正则表达式对象,例:正则表达式有分组时,匹配正则表达式分组

    val pattern="([0-9]+) (a-z+)".r
    "99 bottles" match{
    case pattern(num,item)=>...
    //num为99,item为bottles
    }    

    八.变量声明中的模式

      模式是可以带变量的,可以在变量声明时运用这样的模式,如 val (x,y)=(1,2)

    //赋值商和余数
    val (q,r)=BigInt(10) /%3
    
    //将arr数组的第一个和第二个值分别赋给first和secend
    val Array(first,secend,_)=arr

    九.for表达式中的模式

      可以在for推导式使用带变量的模式。例:

    import scala.collection.JavaConversions.propertiesAsScalaMap
    for((k,v)<-System.getProperties())
    
    println(k+"->"+v)

    十.样例类

      样例类是一种特殊的类,经过优化用于模式匹配。

      例,扩展自常规类(非样例类)的样例类:

    abstract class Amount
    case class Dollar(value:Double) extends Amount
    case class Currency(value:Double,unit:String) extends Amount
    
    //也可以针对单例的样例对象
    case object Nothing extends Amount
    
    //当有一个类型为Amount的对象时,可以用模式匹配来匹配它的类型,并将属性绑定到变量
    amt match{
    case Dollar(v)=>"$"+v
    case Currency(_,u)=>"Oh nose,I got"+u
    case Nothing =>""
    }

      注:样例类的实例使用(),样例对象不使用圆括号;

        当你声明样例类时,如下自动发生:

        • 构造器中的每一个参数都成为val——除非它是显示地声明为var(不建议这样做);
        • 在伴生对象中提供apply方法让你不用new关键字就能构造出相应的对象,如Dollar(29.25)...;
        • 提供unapply方法让模式可以工作;
        • 将生成toString、equals、hashCode和copy方法——除非显式地给出这些方法的定义;
        • 除这些,其他和普通的类一样

    十一.copy方法和带名参数

      样例类的copy方法创建一个与现有对象值相同的新对象,原对象是不可变的,完全可以共享这个对象的引用,但是可以用带名参数修改某些属性,例:

    val price=amt.copy(value=18.23)  

    十二.case语句中的中置表示法

      如果unapply方法产生一个对偶,则你可以在case语句中使用中置表达式,例:

    amt match{
    case a Currency u=>...}//等同于case Currency(a,u)

    十三.匹配嵌套结构

      样例类常被用于嵌套结构,例: 

    abstract class Item
    case class Article(description:String,price:Double) extends Item
    case class Bundle(decription:String,discount:Double.item:Item*) extends Item

      使用@表示法将嵌套绑定到变量。

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

    十四.样例类是邪恶的吗

      样例类适用于那种标记不会改变的结构(如果是可改变的,那么如果想到一种新的Item,就需要回顾所有的item)。

      在合适的地方,样例类十分便捷:

        • 模式匹配通常比继承更容易把我们引向更精简的代码;
        • 构造时不需要用new的复合对象更加易读;
        • 你将免费得到toString、equals、hashCode和copy方法

    十五.密封类

      通过将样例类的通用超类声明为sealed,可以确保列出所有的选择。

      密封类的子类都必须与该密封类相同的文件定义(好做法),如果某个类是密封的,那么在编译期所有子类是可知的,因此编译器可以检查模式语句的完整性。

    十六.模拟枚举

      

    十七.Option类型

      标准库中的Option类型用样例类来表示可能存在、也可能不存在的值。样例子类Some包装了某个值,例如:Some("Fred"),而样例对象None表示没有值(这种表示比使用空字符串的意图更加清晰,比使用null表示缺少的某值更加安全)。

      Option支持泛型,如Some("Fred")类型为Option[String]。Map类的get方法返回一个Option。

    十八.偏函数

      被包在花括号内的一组case语句是一个偏函数——一个并非对所有输入都有定义的函数。它是PartialFunction[A,B]类的一个实例(A是参数类型,B是返回类型),该类有两个方法:apply从匹配的模式计算函数值,而isDefinedAt方法在输入至少匹配其中一个模式赶回true。

    十九.练习

     

     

     

  • 相关阅读:
    LeetCode算法题-Factorial Trailing Zeroes(Java实现)
    LeetCode算法题-Excel Sheet Column Number(Java实现)
    FluentData 学习 第一弹
    加油 加油
    FluentData -Micro ORM with a fluent API that makes it simple to query a database
    RX
    创业哲学
    9种新思想
    事件总线 EventBus
    关于 C#和.net 的 发展
  • 原文地址:https://www.cnblogs.com/lyq-biu/p/11972714.html
Copyright © 2011-2022 走看看