模式匹配在F#是非常普遍的,用来对某个值进行分支匹配或流程控制。
模式匹配的基本用法模式匹配通过match...with表达式来完成,一个完整的模式表达式长下面的样子:
match [something] with | pattern1 -> expression1 | pattern2 -> expression2 | pattern3 -> expression3当你第一次使用模式匹配,你可以认为他就是命令式语言中的switch...case或者说是if...else if...else。只不过模式匹配的能力要比switch...case强大的多。
考虑下面的例子:
显然,x此时的值是"a",因为第一个匹配分支就匹配正确了。在这个表达式里第三个匹配分支有点特殊:
| _ -> "z"通配符_在这里起到了default的作用,上面的所有分支如果都匹配失败,则最终会匹配的这个分支。
1.分支是有顺序的
但是这三个分支的顺序是可以随便改的,也就意味着我们可以把通配符分支放到第一个位置:
在这个例子中,第一个匹配分支会胜出,同时编译器也会给出一个警告:其他的分支从来都不会被用到。
这说明在模式匹配中,分支的顺序是非常重要的,应该把更加具体的匹配分支放在前面,包含通配符的分支应该放在最后面。
2.模式匹配是一个表达式
模式匹配是一个表达式,所有的分支都应该返回同样的类型,考虑下面的例子:
不同的分支应该返回想通类型的值。
3.至少有一个分支能被匹配到
考虑下面的例子:
由于两个分支都没有匹配到,编译器将会给出警告,你至少要写一个能够匹配到的分支,例如为其添加通配符分支。
你可以通过添加通配符分支让编译器不在发出警告,但是在实际实践中,你应该尽可能的添加可能存在的分支,例如你在对一个选择类型做模式匹配:
如果后来某一天你在Choices类型里添加了一个新的选项D,编译器就会对之前的对Choices的模式匹配发出警告,提示你添加新的分支。试想如果你之前加了通配符,编译器就会吞掉这个警告,进而产生bug。
匹配元组(Tuple)模式匹配几乎可以匹配F#所有的类型,例如元组:
let y = match (1,0) with | (1,x) -> printfn "x=%A" x | (_,x) -> printfn "other x=%A" x显然第一个分支会被匹配到。
你可以把多个模式写在同一个分支上,当多个模式是或的关系时用|隔开:
当多个模式是与的关系时用&隔开:
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又被当做参数做下一次递归
1.as关键字
你可以把模式用as关键字指向另一个名称:
2.匹配子类
:?用来匹配类型,例如第一个分支用来匹配int类型:
匹配类型并不是一种好的实践,正如你在OO语言里编写if type ==...一样。
when条件
有时候你需要对匹配完成的值做一些条件判断:
这种情况可以通过在模式中添加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