函数式编程之-模式匹配(Pattern matching)

模式匹配在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 | C let 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 | D let 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 type type Person = {First:string; Last:string} let person = {First="john"; Last="doe"} match person with | {First="john"} -> printfn "Matched John" | _ -> printfn "Not John" //union type type IntOrBool= I of int | B of bool let intOrBool = I 42 match 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

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywxzw.html