一些剛從java轉到scala的同學在開發的過程中猶如深陷沼澤,因為很多的概念或風格不确定,scala裡有很多的坑,模式比對也算一個。我整理了一下自己所了解的概念,以及一些例子。這個系列最好有些scala的基本經驗,或者接觸過一些其他函數式語言。
要了解模式比對(pattern-matching),先把這兩個單詞拆開,先了解什麼是模式(pattern),這裡所的模式并不是設計模式裡的模式。
而是資料結構上的,這個模式用于描述一個結構的組成。
我們很容易聯想到“正則表達”裡的模式,不錯,這個pattern和正則裡的pattern相似,不過适用範圍更廣,可以針對各種類型的資料結構,不像正則表達隻是針對字元串。
比如正規表達式裡 “^a.*” 這個pattern 表示以a開頭、後續一個或多個字元組成的字元串;
list(“a”, _, _*) 也是個pattern,表示第一個元素是”a”,後續一個或多個元素的list。
狹義的看,模式可以當作對某個類型,其内部資料在結構上抽象出來的表達式。如上面的list(“a”, _, _*) 就是一種list結構的pattern。
模式比對(pattern-matching)則是比對變量是否符合這種pattern。比如list(“a”,”b”)和list(“a”,”x”,”y”) 就符合上面的pattern,而list(“x”)則不符合。
直覺的看幾個例子:
例子中的:array(1,2,3) ,list(“a”,_,”c”) 等都是模式,表示由指定元素組成的某種類型。
當然模式也不僅僅是表示某種結構的,還可以是常量,或類型,如:
在 scala裡對pattern有明确的定義,在形式上有以下幾種pattern:
這個是真正能展現模式比對威力的一個模式!
我們來定義一個二叉樹:
這樣我們構造一個根節點含有2個子節點的數:
scala>val tree = tree(treenode(“root”,treenode(“left”,null,null),treenode(“right”,null,null)))
如果我們期望一個樹的構成是根節點的左子節點值為”left”,右子節點值為”right”并且右子節點沒有子節點
那麼可以用下面的方式比對:
scala> tree.root match { case treenode(_,treenode(“left”,_,_), treenode(“right”,null,null)) => println(“bingo”) }
隻要一行代碼就可以很清楚的描述,如果用java實作,是不是沒這麼直覺呢?
5) 類型模式(type patterns)
類型模式很簡單,就是判斷對象是否是某種類型:
scala> “hello” match { case _:string => println(“ok”) }
跟 isinstanceof 判斷類型的效果一樣,需要注意的是scala比對泛型時要注意,
比如
scala> def foo(a:any) { a match { case a :list[string] => println(“ok”); case _ => } }
如果使用了泛型,它會被擦拭掉,如同java的做法,是以上面的 list[string] 裡的string運作時并不能檢測
foo(list(“a”)) 和 foo(list(2)) 都可以比對成功。實際上上面的語句編譯時就會給出警告,但并不出錯。
通常對于泛型直接用通配符替代,上面的寫為 case a : list[_] => …
6) 變量綁定模式 (variable binding patterns)
這個和前邊的變量模式有什麼不同?看一下代碼就清楚了:
依然是上面的treenode,如果我們希望比對到左邊節點值為”left”就傳回這個節點的話:
scala> tree.root match { case treenode(_, leftnode@treenode(“left”,_,_), _) => leftnode }
用@符号綁定 leftnode變量到比對到的左節點上,隻有比對成功才會綁定
另外解釋一下抽取器模式(extractor patterns),一些資料裡也會提到這個模式
抽取器是一種實作模式比對的技術方式,在表現上,抽取器模式與構造器模式一緻,都是 case a(e1,e2) => 這樣的形式。
在《programming in scala》一書中提到 序列模式(sequence patterns),針對所有seqfactory的子類,它其實就是抽取器模式。
在表達形式上 case list(1,2,3) => … 或 case array(“a”,”b”) => … 看着與構造器模式一模一樣(就是背後實作有所不同)
是以在模式的表現形式上,不适合把它劃為一類,非要把 序列模式 與構造器模式區分的話,也是從它們背後的實作上,而不是表現上。
另外《programming in scala》一書中也單獨提到 元組模式(tuple patterns), 元組模式本質上也是一個構造器模式。
了解完模式比對的感念後,我們後續再看一下scala裡是怎麼實作模式比對的