模式匹配在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

when语句尽管可以给模式添加一些条件,但是当语句过于复杂的时候可以考虑某个分支的模式定义为一个方法:

open System.Text.RegularExpressions

// create an active pattern to match an email address
let (|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 //test
classifyString "alice@example.com"
classifyString "google.com"

函数式编程之-模式匹配(Pattern matching)的更多相关文章

  1. 函数式编程之-拒绝空引用异常(Option类型)

    众多语言都会设计Option类型,例如Java 8和Swift都设计了Optional类型.其实这种类型早就出现在了函数式语言中,在OCaml和Scala中叫Option,在Haskell中叫Mayb ...

  2. Scala学习教程笔记三之函数式编程、集合操作、模式匹配、类型参数、隐式转换、Actor、

    1:Scala和Java的对比: 1.1:Scala中的函数是Java中完全没有的概念.因为Java是完全面向对象的编程语言,没有任何面向过程编程语言的特性,因此Java中的一等公民是类和对象,而且只 ...

  3. [Scala] Pattern Matching(模式匹配)

    Scala中的match, 比起以往使用的switch-case有著更強大的功能, 1. 傳統方法 def toYesOrNo(choice: Int): String = choice match ...

  4. 让JavaScript回归函数式编程的本质

    JavaScript是一门被误会最深的语言,这话一点不假,我们看下它的发展历史. 1995年,Netscape要推向市场,需要一门脚本语言来配套它.是使用一门已有的语言,还是发明一门新的语言,这也不是 ...

  5. Scala函数式编程实现排序算法

    记得<Function Thinking>这本书中提到,现在的编程范式有两类,一类是“命令式编程”,另一类是“函数式编程”,现在我们最常使用的许多语言像c.c++.java都是命令式的,但 ...

  6. 从0开发3D引擎(五):函数式编程及其在引擎中的应用

    目录 上一篇博文 函数式编程的优点与缺点 优点 缺点 为什么使用Reason语言 函数式编程学习资料 引擎中相关的函数式编程知识点 数据 不可变数据 可变数据 函数 纯函数 高阶函数 柯西化 参考资料 ...

  7. Atitit 函数式编程与命令式编程的区别attilax总结  qbf

    Atitit 函数式编程与命令式编程的区别attilax总结  qbf 1.1. 函数式程序就是一个表达式.命令式程序就是一个冯诺依曼机的指令序列. 命令式编程是面向计算机硬件的抽象,有变量(对应着存 ...

  8. Scala之模式匹配(Patterns Matching)

    前言 首先.我们要在一開始强调一件非常重要的事:Scala的模式匹配发生在但绝不仅限于发生在match case语句块中.这是Scala模式匹配之所以重要且实用的一个关键因素!我们会在文章的后半部分具 ...

  9. 用函数式编程,从0开发3D引擎和编辑器(二):函数式编程准备

    大家好,本文介绍了本系列涉及到的函数式编程的主要知识点,为正式开发做好了准备. 函数式编程的优点 1.粒度小 相比面向对象编程以类为单位,函数式编程以函数为单位,粒度更小. 正所谓: 我只想要一个香蕉 ...

随机推荐

  1. (转)RandomAccessFile类使用详解

    1.RandomAccessFile特点   RandomAccessFile是java Io体系中功能最丰富的文件内容访问类.即可以读取文件内容,也可以向文件中写入内容.但是和其他输入/输入流不同的 ...

  2. Linux挂载NAS 网络附属存储

    在工作中经常听到NAS,比如做数据交换,将数据从DB2数据库,导入到ORACLE数据库,采用BCP的方式,首先将DB2导出为文件,再从文件导入到ORACLE.那么中间需要一个很大的存储空间来保存从DB ...

  3. android初学

    1布局 LinearLayout 线性布局 FrameLayout 框架布局 AbsoluteLayout 绝对布局 RelativeLayout 相对布局 TableLayout 表格布局 2标记语 ...

  4. 分析Ajax抓取今日头条街拍美图

    spider.py # -*- coding:utf-8 -*- from urllib import urlencode import requests from requests.exceptio ...

  5. Android逆向之smali学习

    Smali是Android虚拟机Dalvik反汇编的结果. Dalvik指令集 指令格式为:[op]-[type](可选)/[位宽,默认4位] [目标寄存器],[源寄存器](可选) 赋值:move*  ...

  6. PMP:6.项目进度管理

    项目管理包括为项目管理项目按时完成所需的各个过程:

  7. 架构(二)Maven安装以及Nexus配置

    一 Maven安装配置 1.1 下载 http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.5.4/binaries/apache-ma ...

  8. Entity Framework 自动生成代码 如何用继承

    分部类 用接口

  9. 【.NET Core项目实战-统一认证平台】第四章 网关篇-数据库存储配置(2)

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章我们介绍了如何扩展Ocelot网关,并实现数据库存储,然后测试了网关的路由功能,一切都是那么顺利,但是有一个问题未解决,就是如果网关 ...

  10. 【面试必备】常见Java面试题大综合

    一.Java基础 1.Arrays.sort实现原理和Collections.sort实现原理答:Collections.sort方法底层会调用Arrays.sort方法,底层实现都是TimeSort ...