Scala模式匹配簡(jiǎn)述

什么是匹配模式?

模式匹配并不很新淆珊,(上世紀(jì))七十年代中期就已經(jīng)有語(yǔ)言采用夺饲。據(jù)我所知,第一種語(yǔ)言是ML施符,但可能也有更早的語(yǔ)言支持往声。它在許多函數(shù)式語(yǔ)言中都算是標(biāo)準(zhǔn)功能,包括ML操刀、Caml烁挟、Erlang、以及Haskell骨坑。

那么什么是模式匹配呢撼嗓?它可以讓你給一個(gè)值匹配多種情況,有點(diǎn)像Java中的switch語(yǔ)句欢唾。但它不僅可以像switch語(yǔ)句一樣用來匹配數(shù)字且警,還可以匹配對(duì)象的內(nèi)在構(gòu)建形式。

比如礁遣,Scala中的List存在兩種情況:要么是空List斑芜,寫做Nil;要么由一個(gè)head元素緊接著另一List tail組成祟霍。有了模式匹配杏头,你可以詢問:給定的List是空List嗎?只要編寫case Nil沸呐、箭頭(=>)以及后續(xù)表達(dá)式即可:

case Nil => // 后續(xù)表達(dá)式

你還可以詢問:它是非空List嗎醇王?只要編寫case x :: xs、箭頭崭添、以及后續(xù)表達(dá)式即可:

case x :: xs => // 后續(xù)表達(dá)式

雙冒號(hào)(::)表示cons操作符寓娩;x表示List的首元素,xs表示剩余部分呼渣。于是棘伴,模式匹配會(huì)首先區(qū)分List是否為空。而如果List空屁置,它會(huì)把List的首元素命名為x然后把List剩余部分命名為xs焊夸。接下來,這些變量可以被箭頭右側(cè)表達(dá)式所用蓝角。(參見示例1)

示例1:match表達(dá)式

list match { 
    case Nil => "was an empty list" 
    case car :: cdr => "head was " + car + ", tail was " + cdr
}

如果list不為空淳地,將匹配到第二種情況怖糊,List首元素將賦值給x,而列表剩余部分賦值給xs颇象。接下來伍伤,這些變量將被箭頭符號(hào)右側(cè)的字符串連接表達(dá)式所用。例如遣钳,如果list內(nèi)容是List("hello", "world")扰魂,那么匹配表達(dá)式的結(jié)果將是字符串"head was hello, tail was List(world)"。

上例的模式非常簡(jiǎn)單蕴茴。但實(shí)際上模式還支持嵌套劝评,類似表達(dá)式的嵌套,能讓你編寫層數(shù)很深的模式倦淀〗螅總的來說,亮點(diǎn)在于撞叽,模式和表達(dá)式看起來很像姻成。模式本質(zhì)上和表達(dá)式屬于完全一類東西,看上去就像構(gòu)造表達(dá)式一樣愿棋,可以用來構(gòu)造復(fù)雜樹狀對(duì)象科展,但卻不需要編寫new。事實(shí)上糠雨,在Scala中才睹,該對(duì)象構(gòu)造時(shí)一樣不需要new。然后你可以在某些位置填上占位變量甘邀,對(duì)應(yīng)樹對(duì)象中實(shí)際存在的值琅攘。(參見示例2)

示例2:嵌套模式的match表達(dá)式

object match { 
    case Address(Name(first, last), street, city, state, zip) => println(last + ", " + zip) 
    case _ => println("not an address") // 默認(rèn)情況
}

在第一種情況下,模式Name(first, last)嵌在模式Address(...)中松邪。last放在了Name構(gòu)造函數(shù)內(nèi)乎澄,可以“提取”出值,因而测摔,可供箭頭右邊的表達(dá)式使用。

因?yàn)槠ヅ涫前l(fā)生在運(yùn)行期的解恰,而且JVM中泛型的類型信息會(huì)被擦掉(跟Java里范型一樣不能匹配)锋八。

case m: Map[String, Int] => ...  // 不行,類型不起作用
case m: Map[_, _] => ...  // 匹配通用的Map护盈,OK

但對(duì)于數(shù)組來說挟纱,類型信息是完好的,所以可以在Array上匹配腐宋。

對(duì)于嵌套結(jié)構(gòu)紊服,舉例就能一目了然檀轨。

abstarct class Item
case class Article(description: String, price: Double) extends Item
case class Bundle(description: String, price: Double, items: Item*) extends Item
 
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 Whisky", 79.95),
    Article("Junipero Gin", 32.95)
  )
)

模式可以匹配到特定的嵌套:

case Bundle(_, _, Article(descr, _), _*) => ...

上面的代碼中descr這個(gè)變量被綁定到第一個(gè)Article的description。另外還可以使用@來將值綁定到變量:

// art被綁定為第一個(gè)Article欺嗤,rest是剩余的Item序列
case Bundle(_, _, art @ Article(_, _), rest @ _*) => ...

樣例類
樣例類是種特殊的類参萄,經(jīng)過優(yōu)化以用于模式匹配。

abstract class Amount
// 繼承了普通類的兩個(gè)樣例類
case class Dollar(value: Double) extends Amount
case class Currency(value: Double, unit: String) extends Amount
 
// 樣例對(duì)象
case object Nothing extends Amount

使用:

amt match {
  case Dollar(v) => "$" + v
  case Currency(_, u) => "Oh noes, I got " + u
  case Nothing => ""  // 樣例對(duì)象沒有()
}

在聲明樣例類時(shí)煎饼,下面的過程自動(dòng)發(fā)生了:
構(gòu)造器的每個(gè)參數(shù)都成為val讹挎,除非顯式被聲明為var,但是并不推薦這么做吆玖;
在伴生對(duì)象中提供了apply方法筒溃,所以可以不使用new關(guān)鍵字就可構(gòu)建對(duì)象;
提供unapply方法使模式匹配可以工作沾乘;
生成toString怜奖、equals、hashCode和copy方法翅阵,除非顯示給出這些方法的定義歪玲。
除了上述之外,樣例類和其他類型完全一樣怎顾,方法字段等读慎。

密封類
當(dāng)使用樣例類來做模式匹配時(shí),如果要讓編譯器確保已經(jīng)列出所有可能的選擇槐雾,可以將樣例類的通用超類聲明為sealed夭委。
密封類的所有子類都必須在與該密封類相同的文件中定義。
如果某個(gè)類是密封的募强,那么在編譯期所有的子類是可知的株灸,因而可以檢查模式語(yǔ)句的完整性。
讓所有同一組的樣例類都擴(kuò)展某個(gè)密封的類或特質(zhì)是個(gè)好的做法擎值。

模式匹配的目的

那么慌烧,為什么你需要模式匹配?我們每個(gè)人都有復(fù)雜的數(shù)據(jù)鸠儿。如果我們堅(jiān)持嚴(yán)格的面向?qū)ο蟮娘L(fēng)格屹蚊,那么我們并不希望直接訪問數(shù)據(jù)內(nèi)部的樹狀結(jié)構(gòu)。相反进每,我們希望調(diào)用方法汹粤,然后在方法中訪問。如果我們能夠這樣做田晚,那么我們就再也不需要模式匹配了嘱兼,因?yàn)檫@些方法已經(jīng)提供了我們需要的功能。但很多情況下贤徒,對(duì)象并不提供我們需要的方法芹壕,而且我們無(wú)法(或者不愿)向這些對(duì)象添加方法汇四。

例如XML。如果給你一棵XML樹踢涌,那么樹就只是單純的數(shù)據(jù)通孽。要么是節(jié)點(diǎn),要么是節(jié)點(diǎn)的序列斯嚎。XML是一種非常通用的數(shù)據(jù)表現(xiàn)形式利虫。例如,DOM本質(zhì)上只是節(jié)點(diǎn)的數(shù)組堡僻,其中每個(gè)節(jié)點(diǎn)的類型都未知】繁梗現(xiàn)在我們?cè)O(shè)想一下,如果把XML樹轉(zhuǎn)換到某種更強(qiáng)的框架中钉疫,可以給你一個(gè)列表硼讽,容納各種不同類型的對(duì)象。組成列表的元素可能包括諸如電話號(hào)碼牲阁、備忘錄或地址等固阁。如果你想以靜態(tài)類型的方式獲取所有這些東西,就會(huì)遇上一個(gè)問題:你不知道每個(gè)元素的類型城菊。在傳統(tǒng)面向?qū)ο蟮木幊陶Z(yǔ)言中备燃,唯一可行方式是,編寫一大堆instanceof檢測(cè)凌唬,一一測(cè)試每個(gè)元素是PhoneNumber實(shí)例并齐、Memo實(shí)例,還是其他實(shí)例客税。一旦這些instanceof語(yǔ)句之一檢測(cè)成功况褪,你還需要進(jìn)行類型轉(zhuǎn)換。上述做法相當(dāng)丑陋和笨拙更耻,有了模式匹配就能避免了测垛。模式匹配能以更安全、更自然的方式完成相同功能秧均。

從本質(zhì)上講食侮,當(dāng)你從外部取得具有結(jié)構(gòu)的對(duì)象圖時(shí),模式匹配就必不可少目胡。你會(huì)在若干情況下遇到這種現(xiàn)象锯七,XML是其中之一。各種從文本解析而來的數(shù)據(jù)讶隐,都屬于這一類。例如久又,有一種典型情況下模式匹配必不可少巫延,即效五,處理編譯器中的抽象語(yǔ)法樹的情況。如果你要對(duì)表達(dá)式進(jìn)行化簡(jiǎn)操作炉峰,表達(dá)式會(huì)被表示為樹畏妖,你需要通過模式匹配對(duì)這些樹進(jìn)行提取操作。類似那樣的情況還有許多疼阔。遇到這些情況時(shí)戒劫,模式匹配真的必不可少。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末婆廊,一起剝皮案震驚了整個(gè)濱河市迅细,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淘邻,老刑警劉巖茵典,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異宾舅,居然都是意外死亡统阿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門筹我,熙熙樓的掌柜王于貴愁眉苦臉地迎上來扶平,“玉大人,你說我怎么就攤上這事蔬蕊〗岢危” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵袁串,是天一觀的道長(zhǎng)概而。 經(jīng)常有香客問我,道長(zhǎng)囱修,這世上最難降的妖魔是什么赎瑰? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮破镰,結(jié)果婚禮上餐曼,老公的妹妹穿的比我還像新娘。我一直安慰自己鲜漩,他們只是感情好源譬,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孕似,像睡著了一般踩娘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喉祭,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天养渴,我揣著相機(jī)與錄音雷绢,去河邊找鬼。 笑死理卑,一個(gè)胖子當(dāng)著我的面吹牛翘紊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播藐唠,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼帆疟,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了宇立?” 一聲冷哼從身側(cè)響起踪宠,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎泄伪,沒想到半個(gè)月后殴蓬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蟋滴,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年染厅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片津函。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肖粮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尔苦,到底是詐尸還是另有隱情涩馆,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布允坚,位于F島的核電站魂那,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏稠项。R本人自食惡果不足惜涯雅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望展运。 院中可真熱鬧活逆,春花似錦、人聲如沸拗胜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)埂软。三九已至锈遥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背所灸。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工儿礼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人庆寺。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像诉字,于是被迫代替她去往敵國(guó)和親懦尝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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

  • 本章要點(diǎn) match表達(dá)式是一個(gè)更好的switch壤圃,不會(huì)有意外掉入到下一個(gè)分支的問題陵霉。 如果沒有模式能夠匹配,會(huì)拋...
    胡楊1015閱讀 1,140評(píng)論 0 1
  • scala學(xué)習(xí)筆記 第2章 變量和數(shù)據(jù)類型 基本數(shù)據(jù) scala的核心數(shù)據(jù)為四種 :字面量伍绳、值踊挠、變量、類型 值使...
    485b1aca799e閱讀 2,115評(píng)論 0 1
  • 這篇講義只講scala的簡(jiǎn)單使用冲杀,目的是使各位新來的同事能夠首先看懂程序效床,因?yàn)?scala 有的語(yǔ)法對(duì)于之前使用習(xí)...
    MrRobot閱讀 2,906評(píng)論 0 10
  • 從匹配中返回值 Match 對(duì)象 成功的匹配總是返回一個(gè) Match 對(duì)象, 這個(gè)對(duì)象通常也被放進(jìn) $/ 中, (...
    焉知非魚閱讀 1,790評(píng)論 0 1
  • 岳: 媽媽剩檀,我不想評(píng)三好生了可以嗎? m: 為什么呢旺芽?只要有機(jī)會(huì)當(dāng)然要積極爭(zhēng)取啦沪猴?三好生是對(duì)優(yōu)秀學(xué)生的一個(gè)證明。 ...
    manxue閱讀 433評(píng)論 0 0