Kotlin(三)代數(shù)數(shù)據(jù)類型和模式匹配

代數(shù)數(shù)據(jù)類型(Algebraic Data Type,ADT)夭苗。用密封類和數(shù)據(jù)類構(gòu)建代數(shù)數(shù)據(jù)類型

3.1 代數(shù)數(shù)據(jù)類型

ADT 其實(shí)是一個(gè)組合類型菩彬。

高中課本我們接觸過代數(shù)和幾何學(xué)色迂。一個(gè)公式a + 2 = c 把這類公式轉(zhuǎn)換成為編程語言中的類型或者值,他們中的某種操作鹉动,會(huì)得到某種新的類型摹闽。

簡單說:代數(shù)或者數(shù)字轉(zhuǎn)換成類型,這種被我們代數(shù)或者數(shù)字轉(zhuǎn)換成的類型以及通這些類型產(chǎn)生的新的類型根盒,就叫做代數(shù)數(shù)據(jù)類型(ADT)

每種類型在實(shí)例化的時(shí)候钳幅,都有對應(yīng)的值。Boolean可能有true和false兩種類型的取值炎滞。如果我們將數(shù)字2與取值種類關(guān)聯(lián)就叫做計(jì)數(shù)敢艰,同理Unit 表示只有一個(gè)實(shí)例,那么他的計(jì)數(shù)是1.

ADT 常見的兩種類型: 積類型 和 和類型

積類型我們可以理解和乘法相似册赛,是一種組合類型
Boolean 和 Unity 組合會(huì)產(chǎn)生兩種類型钠导。表示如下:

class BooleanProductUnit(a:Boolean,b:Unit){}

val b0 = BooleanProductUnit(true,Unit)
val b1 = BooleanProductUnit(false,Unit)

實(shí)際上這是一種product操作震嫉,積類型就是同時(shí)持有某些類型的類型

和類型 相當(dāng)于加法
比如我們 定義enum 類型表示周

enum class Day{SUN,MON,TUE,WEN,THU,FRI,SAT}

每一天只能取一個(gè)值,合起來是7種取值

  • 和類型 是類型安全的牡属。 他是一個(gè)閉環(huán)票堵,事先知道可能取值,在使用時(shí)候不用擔(dān)心出現(xiàn)非法情況
  • 和類型 是一種OR的關(guān)系逮栅,對比 積類型 是一種 AND 關(guān)系

雖然枚舉類是和類型悴势,但是功能單一,擴(kuò)展性不強(qiáng)措伐。但是密封類表達(dá)上有更好的表現(xiàn)

使用密封類或者說和表達(dá)式特纤,在使用when表達(dá)式的時(shí)候不用考慮非法的情況,可以省略else侥加。如果遺漏或者多添加了額外情況捧存,編譯器檢測會(huì)幫助報(bào)錯(cuò)

fun schedule(day:Day):String = when(day){
    Day.SUN -> "fishing"
    Day.MON -> "work"
    Day.TUE -> "study"
    Day.WEN -> "library"
    Day.THU -> "writing"
    Day.FRI -> "appointment"
    Day.SAT -> "basketball"
}

另一個(gè)小例子,計(jì)算面積:圓形担败,三角形和長方形

sealed class Shape{
    class Circle(val radius:Double):Shape()
    class Rectangle(val width:Double,val height:Double):Shape()
    class Triangle(val base:Double,val height:Double):Shape()
}

fun getArea(shape:Shape):Double = when(shape){
    is Shape.Circle -> Math.PI * shape.radius
    is Shape.Rectangle -> shape.width * shape.height
    is Shape.Triangle -> shape.base * shape.height
} 

3.2 模式匹配

我們應(yīng)該很熟悉Java或者JS等都有正則表達(dá)式昔穴。模式匹配和他很相似,而且不只有匹配正則表達(dá)式氢架,還可以匹配其他表達(dá)式傻咖。這里面的表達(dá)式就是模式

一個(gè)數(shù)字,一個(gè)對象實(shí)例岖研,凡是能求出特定值的組合,我們叫做表達(dá)式

class Pattern(val text:String)
val a= 1
val b =2
表達(dá)式:5,a+b,Pattern("hello"),a>b
  1. 常量模式:之前的Day和when組合的例子
  2. 類型模式:之前的shape圖像面積計(jì)算Sealed和when結(jié)合
  3. 邏輯表達(dá)式模式:
fun logicPattern(a:Int)= when (a) {
    in 2..11 -> "a in 2 .. 11"
    else -> "not in pattern"
}
  1. 嵌套表達(dá)式模式

定義一個(gè)簡單的整數(shù)表達(dá)式

sealed class Expr {
    data class Num(val alue: Int) : Expr()
    data class Operate(val optName: String, val left: Expr, val right: Expr) : Expr()
}

Num 表示某個(gè)整數(shù)警检,Operate 是一個(gè)樹形結(jié)構(gòu)孙援,optName操作符+ ,- ,* ,/ 等。用來表示一些復(fù)雜表達(dá)式扇雕。

定義非常簡單拓售,但是具體邏輯實(shí)現(xiàn)起來很繁瑣,無論是java還是镶奉,kotlin

------》 轉(zhuǎn)移一下視線 對比下Scala 因?yàn)樗鼘τ谀J狡ヅ渲С值母么∮伲覀兛聪滤悸贰?br> Scala 語法 模式匹配

// test
sealed trait Expr
case class Num(value:Int) extends Expr
case class Operate(optName:String,left:Expr,right:Expr) extends Expr

def simplifyExpr(expr:Expr) = expr match{
    // 0 + x
    case Operate ("+",Num(0),x) => x
    // x + 0
    case Operate ("+",x,Num(0)) => x
    case _ => expr
}

正常實(shí)例化一個(gè)對象

val expr = Expr.Operate("+", Expr.Num(0), x)

反向看一下

val ("+", Expr.Num(0), x) = expr

會(huì)想到之前元祖的解構(gòu),kotlin里前兩個(gè)文章中提到的解構(gòu)

所以哨苛,推導(dǎo)下來的結(jié)論就是:
模式匹配中的模式就是表達(dá)式鸽凶,模式匹配要匹配的就是表達(dá)式。模式匹配的核心就是解構(gòu),就是反向構(gòu)造表達(dá)式建峭。

我們需要用when去寫模式匹配玻侥,還有其他的我們之后在研究。

fun simplefyExpr(expr: Expr):Expr = when (expr) {
  is Expr.Num -> expr
  is Expr.Operate -> when(expr){
      Expr.Operate("+",Expr.Num(0),expr.right) -> expr.right
      Expr.Operate("+",expr.left,Expr.Num(0)) -> expr.left
      else -> expr
  }
}

如果我們要完成這個(gè)表達(dá)式

val express = Expr.Operate("+",Expr.Num(0),Expr.Operate("+",Expr.Num(0),Expr.Num(0)))

先看when用遞歸和非遞歸實(shí)現(xiàn)

fun simplefyExpr2(expr: Expr):Expr = when (expr) {
    is Expr.Num -> expr
    is Expr.Operate -> when(expr){
        Expr.Operate("+",Expr.Num(0),expr.right) -> simplefyExpr2(expr.right)
        Expr.Operate("+",expr.left,Expr.Num(0)) -> expr.left
        else -> expr
    }
}

實(shí)際業(yè)務(wù)沒有這么對稱亿蒸,而且我們指討論“+” 還是有些復(fù)雜的

非遞歸方式

fun simplefyExpr3(expr: Expr): Expr = when (expr) {
    is Expr.Num -> expr
    is Expr.Operate -> when (expr) {
        (expr.left is Expr.Num && expr.left == 0) && (expr.right is Expr.Operate)
        -> when (expr.right) {
            Expr.Operate("+", expr.right.left, Expr.Num(0))
            -> expr.right.left
            else -> expr.right
        }
        else -> expr
    }
}

增強(qiáng)模式匹配

實(shí)現(xiàn)模式匹配的技術(shù)
類型測試或類型轉(zhuǎn)換凑兰,面向?qū)ο蟮姆纸庹谱L問者設(shè)計(jì)模式,TypeCase姑食,樣本類波岛,抽取器。

前三種目前還有點(diǎn)搞頭音半,后面三個(gè)不是考慮的范疇了

類型測試或類型轉(zhuǎn)換---> 前面的Expr 的例子

面向?qū)ο蟮姆纸?/p>

sealed class Expr2 {
    abstract fun isZero(): Boolean
    abstract fun isAddZero(): Boolean
    abstract fun left(): Expr2
    abstract fun right(): Expr2


    data class Num(val value: Int) : Expr2() {
        override fun isZero(): kotlin.Boolean = this.value == 0

        override fun isAddZero(): Boolean = false

        override fun left(): Expr2 = throw Throwable("no element")

        override fun right(): Expr2 = throw Throwable("no element")
    }

    data class Operate(val optName: String, val left: Expr2, val right: Expr2) : Expr2() {
        override fun isZero(): kotlin.Boolean = false

        override fun isAddZero(): Boolean = this.optName ==
                "+" && (this.left.isZero() || this.right.isZero())

        override fun left(): Expr2 = this.left

        override fun right(): Expr2 = this.right
    }
}

fun simplefyExpr3(expr: Expr2): Expr2 = when  {
    expr.isAddZero() && expr.right().isAddZero() && expr.right().left().isZero()
    -> expr.right().right()
    else -> expr

}

實(shí)際業(yè)務(wù)很復(fù)雜则拷,如果我們需要將類的方法,在每個(gè)子類再重新實(shí)現(xiàn)一遍祟剔。代價(jià)很高隔躲。比較簡單的 業(yè)務(wù)還湊合

訪問者模式

改造上面的實(shí)現(xiàn),增加一個(gè)Visitor類物延,實(shí)現(xiàn)訪問者模式

sealed class Expr3 {
    abstract fun isZero(v: Visitor): Boolean
    abstract fun isAddZero(v: Visitor): Boolean
    abstract fun simplefyExpr(v: Visitor): Expr3


    data class Num(val value: Int) : Expr3() {
        override fun isZero(v: Visitor): kotlin.Boolean = v.matchZero(this)

        override fun isAddZero(v: Visitor): Boolean = v.matchAddZero(this)

        override fun simplefyExpr(v: Visitor): Expr3 =v.doSimplefyExpr(this)

    }

    data class Operate(val optName: String, val left: Expr3, val right: Expr3) : Expr3() {
        override fun isZero(v:Visitor): kotlin.Boolean = v.matchZero(this)

        override fun isAddZero(v:Visitor): Boolean = v.matchAddZero(this)
        
        override fun simplefyExpr(v: Visitor): Expr3 = v.doSimplefyExpr(this,v)
    }
}


class Visitor {
    fun matchAddZero(expr: Expr3.Num): Boolean = false
    fun matchAddZero(expr: Expr3.Operate): Boolean = when (expr) {
        Expr3.Operate("+", Expr3.Num(0), expr.right)
        -> true
        Expr3.Operate("+", expr.left, Expr3.Num(0))
        -> false
        else -> false
    }
    
    fun matchZero(expr:Expr3.Num):Boolean = expr.value == 0
    fun matchZero(expr:Expr3.Operate):Boolean = false
    
    fun doSimplefyExpr(expr:Expr3.Num):Expr3= expr
    fun doSimplefyExpr(expr:Expr3.Operate,v:Visitor):Expr3= when{
        (expr.right is Expr3.Num && v.matchZero(expr) && v.matchAddZero(expr.right))
                && (expr.right is Expr3.Operate && expr.right.left is Expr3.Num)
                && v.matchAddZero(expr.right.left)
        -> expr.right.left
        else -> expr
    }

}

采用訪問者設(shè)計(jì)模式
1.我們類中的設(shè)計(jì)方法宣旱,方法放到了類外實(shí)現(xiàn),讓類機(jī)構(gòu)更清晰
2.子類特別多并且結(jié)構(gòu)復(fù)雜 時(shí)候叛薯,訪問者可以減少我們寫很多判斷邏輯浑吟,特定子類進(jìn)行相關(guān)操作,會(huì)方便一些
3.缺點(diǎn)耗溜,新增加子類组力,在訪問類中增加這個(gè)子類的操作,而且一些測試方法可能要重寫抖拴,所以Visitor模式也要慎用

訪問者設(shè)計(jì)及模式+when結(jié)合----》{數(shù)據(jù)結(jié)構(gòu)在后期不會(huì)變動(dòng)太大燎字,業(yè)務(wù)邏輯相對復(fù)雜的情況}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者阿宅。
  • 序言:七十年代末候衍,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子洒放,更是在濱河造成了極大的恐慌蛉鹿,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件往湿,死亡現(xiàn)場離奇詭異妖异,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)领追,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門他膳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蔓腐,你說我怎么就攤上這事矩乐。” “怎么了?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵散罕,是天一觀的道長分歇。 經(jīng)常有香客問我,道長欧漱,這世上最難降的妖魔是什么职抡? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮误甚,結(jié)果婚禮上缚甩,老公的妹妹穿的比我還像新娘。我一直安慰自己窑邦,他們只是感情好擅威,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著冈钦,像睡著了一般郊丛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瞧筛,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天厉熟,我揣著相機(jī)與錄音,去河邊找鬼较幌。 笑死揍瑟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的乍炉。 我是一名探鬼主播绢片,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼岛琼!你這毒婦竟也來了杉畜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤衷恭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后纯续,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體随珠,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年猬错,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窗看。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡倦炒,死狀恐怖显沈,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤拉讯,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布涤浇,位于F島的核電站,受9級特大地震影響魔慷,放射性物質(zhì)發(fā)生泄漏只锭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一院尔、第九天 我趴在偏房一處隱蔽的房頂上張望蜻展。 院中可真熱鬧,春花似錦邀摆、人聲如沸纵顾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽施逾。三九已至,卻和暖如春贞盯,著一層夾襖步出監(jiān)牢的瞬間音念,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工躏敢, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闷愤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓件余,卻偏偏與公主長得像讥脐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子啼器,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內(nèi)容