函数式编程之-模式匹配(Pattern matching)
編者:C# 7.0也加入了模式匹配,來源于F#。
模式匹配在F#是非常普遍的,用來對某個值進行分支匹配或流程控制。
模式匹配的基本用法
模式匹配通過match...with表達式來完成,一個完整的模式表達式長下面的樣子:
| match [something] with | pattern1 -> expression1| pattern2 -> expression2| pattern3 -> expression3 |
當你第一次使用模式匹配,你可以認為他就是命令式語言中的switch...case或者說是if...else if...else。只不過模式匹配的能力要比switch...case強大的多。
考慮下面的例子:
| let?x = ????match 1 with ????| 1 -> "a"????| 2 -> "b"?????| _ -> "z" |
顯然,x此時的值是"a",因為第一個匹配分支就匹配正確了。在這個表達式里第三個匹配分支有點特殊:
| | _ -> "z" |
通配符_在這里起到了default的作用,上面的所有分支如果都匹配失敗,則最終會匹配的這個分支。
1.分支是有順序的
但是這三個分支的順序是可以隨便改的,也就意味著我們可以把通配符分支放到第一個位置:
| let?x = ???match 1 with ???| _ -> "z"???| 1 -> "a"???| 2 -> "b" |
在這個例子中,第一個匹配分支會勝出,同時編譯器也會給出一個警告:其他的分支從來都不會被用到。
這說明在模式匹配中,分支的順序是非常重要的,應該把更加具體的匹配分支放在前面,包含通配符的分支應該放在最后面。
2.模式匹配是一個表達式
模式匹配是一個表達式,所有的分支都應該返回同樣的類型,考慮下面的例子:
| let?x = ????match 1 with ????| 1 -> 42????| 2 -> true??// error wrong type????| _ -> "hello"?// error wrong type |
不同的分支應該返回想通類型的值。
3.至少有一個分支能被匹配到
考慮下面的例子:
| let?x = ????match 42 with ????| 1 -> "a"????| 2 -> "b" |
由于兩個分支都沒有匹配到,編譯器將會給出警告,你至少要寫一個能夠匹配到的分支,例如為其添加通配符分支。
你可以通過添加通配符分支讓編譯器不在發出警告,但是在實際實踐中,你應該盡可能的添加可能存在的分支,例如你在對一個選擇類型做模式匹配:
| type Choices = A | B | Clet?x = ????match A with ????| A -> "a"????| B -> "b"????| C -> "c" |
如果后來某一天你在Choices類型里添加了一個新的選項D,編譯器就會對之前的對Choices的模式匹配發出警告,提示你添加新的分支。試想如果你之前加了通配符,編譯器就會吞掉這個警告,進而產生bug。
匹配元組(Tuple)
模式匹配幾乎可以匹配F#所有的類型,例如元組:
| let?y = ????match (1,0) with ????| (1,x) -> printfn "x=%A"?x????| (_,x) -> printfn "other x=%A"?x |
顯然第一個分支會被匹配到。
你可以把多個模式寫在同一個分支上,當多個模式是或的關系時用|隔開:
| type Choices = A | B | C | Dlet?x = ????match A with ????| A | B | C -> "a or b or c"????| D -> "d" |
當多個模式是與的關系時用&隔開:
| let?y = ????match (1,0) with ????| (2,x) & (_,1) -> printfn "x=%A"?x |
匹配list
匹配list只有三種模式:
[x;y;z]用來顯示匹配list中的元素
head::tail head會匹配到第一個元素,其他的元素會匹配到tail,這個模式常用來對list做遞歸
[] 會匹配到空的list
| let?rec loopAndPrint aList = ????match aList with ????| [] -> ????????printfn "empty"????| x::xs -> ????????printfn "element=%A,"?x????????loopAndPrint xs loopAndPrint [1..5] |
當[]模式被匹配到,說明list已經為空,可以作為遞歸的終止條件;
x::xs模式會將第一個元素匹配到x中,剩余的元素被匹配到xs,然后xs又被當做參數做下一次遞歸
匹配Recoard type和Descriminated Union type...
| //record typetype Person = {First:string; Last:string}let?person = {First="john"; Last="doe"}match person with | {First="john"}? -> printfn "Matched John"| _? -> printfn "Not John"//union typetype IntOrBool= I of int?| B of boollet?intOrBool = I 42match intOrBool with | I i? -> printfn "Int=%i"?i| B b? -> printfn "Bool=%b"?b |
其他
1.as關鍵字
你可以把模式用as關鍵字指向另一個名稱:
| let?y = ????match (1,0) with ????| (x,y) as?t -> ????????printfn "x=%A and y=%A"?x y????????printfn "The whole tuple is %A"?t |
2.匹配子類
:?用來匹配類型,例如第一個分支用來匹配int類型:
| let?detectType v =????match box v with????????| :? int?-> printfn "this is an int"????????| _ -> printfn "something else" |
匹配類型并不是一種好的實踐,正如你在OO語言里編寫if type ==...一樣。
when條件
有時候你需要對匹配完成的值做一些條件判斷:
| let?elementsAreEqual aTuple = ????match aTuple with ????| (x,y) -> ????????if?(x=y) then printfn "both parts are the same"????????else?printfn "both parts are different" |
這種情況可以通過在模式中添加when條件來做到:
| let?elementsAreEqual aTuple = ????match aTuple with ????| (x,y) when x=y -> ????????printfn "both parts are the same"????| _ ->????????printfn "both parts are different" |
Active pattern
when語句盡管可以給模式添加一些條件,但是當語句過于復雜的時候可以考慮某個分支的模式定義為一個方法:
| open System.Text.RegularExpressions// create an active pattern to match an email addresslet?(|EmailAddress|_|) input =???let?m = Regex.Match(input,@".+@.+") ???if?(m.Success) then Some input else?None? // use the active pattern in the match? let?classifyString aString = ????match aString with ????| EmailAddress x -> ????????printfn "%s is an email"?x?????????????// otherwise leave alone????| _ -> ????????printfn "%s is something else"?aString//testclassifyString "alice@example.com"classifyString "google.com" |
原文地址:https://www.cnblogs.com/xiandnc/p/9388259.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結
以上是生活随笔為你收集整理的函数式编程之-模式匹配(Pattern matching)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 树莓派3B+,我要跑.NET CORE
- 下一篇: .net core 多版本如何选择