函数式编程之-模式匹配(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

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

open System.Text.RegularExpressions

// create an active pattern to match an email address
let (|EmailAddress|_|) input =
   let m = Regex.Match(input,@"[email protected]+")
   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 "[email protected]"
classifyString "google.com"

原文地址:https://www.cnblogs.com/xiandnc/p/9388259.html

时间: 2024-10-11 08:38:18

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

[Scala] Pattern Matching(模式匹配)

Scala中的match, 比起以往使用的switch-case有著更強大的功能, 1. 傳統方法 def toYesOrNo(choice: Int): String = choice match { case 1 => "yes" case 0 => "no" case _ => "error" } // toYesOrNo(1)=>"yes" // toYesOrNo(0)=>"n

Scala 函数式程序设计原理(4)--Types and Pattern Matching

4.1 Objects Everywhere Pure Object Orientation: A pure object-oriented language is one in which every value is an object. If the language is based on classes, this means that the type of each value is a class. 4.2 Functions as Objects (x: Int) => x *

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

Atitit 函数式编程与命令式编程的区别attilax总结  qbf 1.1. 函数式程序就是一个表达式.命令式程序就是一个冯诺依曼机的指令序列. 命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列. 而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式. 1.2. 面向对象语言中,数据类型分为两种--基本类型和对象类型(

Scala之模式匹配(Patterns Matching)

前言 首先,我们要在一开始强调一件很重要的事:Scala的模式匹配发生在但绝不仅限于发生在match case语句块中,这是Scala模式匹配之所以重要且有用的一个关键因素!我们会在文章的后半部分详细地讨论这一点. 模式匹配的种类 在Scala中一共有如下几种类型的模式匹配: 通配符匹配(Wildcard Pattern Matching ) 常量匹配 (Constant Pattern Matching ) 变量匹配(Variable Pattern Matching ) 构造函数匹配(Con

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

大家好,本文介绍了本系列涉及到的函数式编程的主要知识点,为正式开发做好了准备. 函数式编程的优点 1.粒度小 相比面向对象编程以类为单位,函数式编程以函数为单位,粒度更小. 正所谓: 我只想要一个香蕉,而面向对象却给了我整个森林 2.性能好 大部分人认为函数式编程差,主要基于下面的理由(参考 JavaScript 函数式编程存在性能问题么?): 1)柯西化.函数组合等操作增加时间开销 2)map.reduce等操作,会进行多次遍历,增加时间开销 3)Immutable数据每次操作都会被拷贝为新的

PYTHON修饰器的函数式编程

转自:http://coolshell.cn/articles/11265.html Python修饰器的函数式编程 Python的修饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西.虽然好像,他们要干的事都很相似--都是想要对一个已有的模块做一些"修饰工作",所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能

我以为的函数式编程

函数式编程 函数式编程(functional programming)的思想相对于命令式编程(imperative programming),告诉计算机你要什么而不是告诉它要怎么做,举个例子: (defun fun(x) (list 'a (expt (car x) 2))) 这是函数是编程,而 (defun imp (x) (let* ((y (car x)) (z (expt y 2))) (list 'a z))) 是命令式编程,结果一样,但思想不同. 函数式编程还要尽量避免对函数的参数进

Programming in Scala (Second Edition) 读书笔记15 case class and pattern matching

一个算术表达式包含: 数字,变量,二元操作符,一元操作符.用下面几个类来模拟它们 package chapter15 abstract class Expr case class Var(name: String) extends Expr case class Number(num: Double) extends Expr case class UnOp(operator: String, arg: Expr) extends Expr case class BinOp(operator: 

Scala Learning(1): 使用Pattern Matching表达JSON

这是一个挺能展现Scala编程方式的例子,对正在熟悉Scala这门语言的开发者很有帮助. Representing JSON 用Scala来表达JSON(Java Script Object Notation)结构, { "firstname" : "John", "lastname" : "Smith", "address" : { "street" : "21 2nd St