理解F#中的模式匹配与活动模式

模式匹配(Pattern Matching)允许我们根据标识符值的不同进行不同的运算,它通常被拿来跟C#中的if…else或switch语法结构相比较,结论往往是模式匹配比后者要更为灵活、强大。那先来分析一下它灵活、强大在哪儿。

为什么说模式匹配是灵活、强大的?

在我前面写过的几篇随笔里面,有几次提到了模式匹配,比如它能够对简单值(整数、字符串)匹配,也可以对.NET类型进行匹配,看下面两个简单的例子:

F# Code - 对简单值和.NET类型进行匹配
// 对简单值进行匹配。
let rec fibonacci x =
  match x with
  | x when x <= 0 -> failwith "x必须是正整数。"
  | 1 -> 1
  | 2 -> 1
  | x -> fibonacci(x - 1) + fibonacci(x - 2)

printfn "%i" (fibonacci 2) // -> 1
printfn "%i" (fibonacci 4) // -> 3
// 对.NET类型进行匹配。
open System
let typeToString x =
  match box x with
  | :? Int32 -> "Int32"
  | :? Double -> "Double"
  | :? String -> "String"
  | _ -> "Other Type"

可以看到,这里所用的模式匹配没有给人太多惊喜,不用费多大力气就可以将其转换为if…else或switch结构了。

先别急着离开,列表是FP中的典型数据结构,我们对它应用一下模式匹配看看。

F# Code - 对列表应用模式匹配
// 对列表应用模式匹配。
let listOfList = [[2; 3; 5]; [7; 11; 13]; [17; 19; 23; 29]]
let rec concatenateList list =
  match list with
  | head :: tail -> head @ (concatenateList tail)
  | [] -> []

let rec concatenateList2 list =
  if List.nonempty list then
    let head = List.hd list in
    let tail = List.tl list in
    head @ (concatenateList2 tail)
  else
    []

let primes = concatenateList listOfList
print_any primes // [2; 3; 5; 7; 11; 13; 17; 19; 23; 29]

listOfList是一个列表的列表,两个函数concatenateList和concatenateList2的功能都是将listOfList的元素连接为一个大的列表,只不过一个用模式匹配方式实现,一个使用if…then…else结构实现。可以看到concatenateList的代码更为简洁,但仅仅如此吗?在concatenateList2中,我们按照传统的看待链表(F#中的列表以链表实现)的方式,将其中的节点一个一个取出来进行处理,这种处理方式是较为具体和细节的;而在concatenateList中我们通过两个简单的模式“head :: tail”和“[]”就覆盖了列表的所有可能,可以说,我们找到了更好地分解列表这种数据结构的方式,从而可以更为通用地处理列表。

类似的,再来看看Union类型的情况。Union类型,有时称为sum类型或discriminated union,可将一组具有不同含义或结构的数据组合在一起。它的一个典型应用是表示一颗树:

F# Code - 对Union类型应用模式匹配
type BinaryTree<'a> =
  | Leaf of 'a
  | Node of BinaryTree<'a> * BinaryTree<'a>

let rec printBinaryTreeValues t =
  match t with
  | Leaf x -> printfn "%i" x
  | Node (l, r) ->
    printBinaryTreeValues l
    printBinaryTreeValues r

printBinaryTreeValues (Node ((Node (Leaf 1, Leaf 2)), (Node (Leaf 3, Leaf 4))))

这里通过BinaryTree<'a>定义一个泛型二叉树类型,printBinaryTreeValues函数用于打印其节点的值,这里需要判断节点的类型(子树还是叶子),有趣的是,Leaf和Node自动抽象为“模式”,不需要任何额外的工作。这样就可以看到一些所谓“灵活、强大”的影子了,对于Union类型所表示的数据结构,模式匹配可以极为简单、自然地分解、处理它。

除了列表和Union类型,元组对于模式匹配的“自适应”也是类似的,这些已经够我们解决很多问题了。那对于其它的更复杂的场景或者更特殊的领域,F#还有什么大招呢?你一定能想得到,这就是活动模式。

时间: 2024-09-11 00:07:32

理解F#中的模式匹配与活动模式的相关文章

F#中的异步及并行模式(3

在本系列的第3部分中,我们会来探索F#中轻量级的,交互式的代理,以及与代理有关的一些模式,包括隔离的内部状态.(译注:由于原文内容较多,译文拆成两段进行.在上半段文章中讨论了代理的基本使用方式,而下半段则讨论关于代理使用中更进一步的模式.) 消息与联合类型 很多时候我们会使用联合类型(Union Type)作为消息的类型.例如,我将要展示一个基于代理的DirectX示例,我们要在模拟引擎中使用如下的消息: type Message = | PleaseTakeOneStep | PleaseAd

F#中的异步及并行模式(1):并行CPU及IO计算

介绍 F#是一门并行(parallel)及响应式(reactive)语言.这个说法意味着一个F#程序可以存在多个进行中的运算(如使用.NET线程进行F#计算),或是多个等待中的回应(如等待事件或消息的回调函数及代理对象). F#的异步表达式是简化异步及响应式程序编写的方式之一.在这篇及今后的文章中,我会探讨一些使用F#进行异步编程的基本方式──大致说来,它们都是F#异步编程时使用的模式.这里我假设您已经掌握了async的基本使用方式,如入门指南中的内容. 我们从两个简单的设计模式开始:CPU异步

F#中的异步及并行模式(2):反馈进度的事件

在这篇文章中,我们将关注一个常见的异步模式:反馈进度的事件(Reporting Progress with Events).在文章最后,我们会使用这个设计模式开发一个示例,从Twitter中获取一系列记录. 这是F#异步编程基础系列的第二部分.其中部分示例代码来自于F# JAOO Tutorial. 第1部分描述了F#作为一个并行及异步语言,是如何支持轻量级的响应操作,并给出了CPU异步并行和I/O异步并行两种模式. 第2部分便是本文. 第3部分则描述了F#中轻量级,响应式的独立代理对象. 模式

深入理解Android中的建造者模式_Android

前言 在Android开发过程中,我发现很多安卓源代码里应用了设计模式,比较常用的有适配器模式(各种adapter),建造者模式(Alert Dialog的构建)等等.虽然我们对大多数设计模式都有所了解,但是在应用设计模式的这个方面,感觉很多人在这方面有所不足.所以这篇文章我们一起深入的理解Android中的建造者模式. 建造者模式(Builder Pattern)也叫生成器模式,其定义如下: separate the construction of a complex object from

深入理解Android中的建造者模式

前言 在Android开发过程中,我发现很多安卓源代码里应用了设计模式,比较常用的有适配器模式(各种adapter),建造者模式(Alert Dialog的构建)等等.虽然我们对大多数设计模式都有所了解,但是在应用设计模式的这个方面,感觉很多人在这方面有所不足.所以这篇文章我们一起深入的理解Android中的建造者模式. 建造者模式(Builder Pattern)也叫生成器模式,其定义如下: separate the construction of a complex object from

理解JavaScript中Promise的使用_javascript技巧

Javascript 采用回调函数(callback)来处理异步编程.从同步编程到异步回调编程有一个适应的过程,但是如果出现多层回调嵌套,也就是我们常说的厄运的回调金字塔(Pyramid of Doom),绝对是一种糟糕的编程体验.于是便有了 CommonJS 的 Promises/A 规范,用于解决回调金字塔问题.本文先介绍 Promises 相关规范,然后再通过解读一个迷你的 Promises 以加深理解. 什么是 Promise一个 Promise 对象代表一个目前还不可用,但是在未来的某

Perl中的模式匹配学习笔记_perl

一.简介模式指在字符串中寻找的特定序列的字符,由反斜线包含:/def/即模式def.其用法如结合函数split将字符串用某模式分成多个单词:@array = split(/ /, $line); 二.匹配操作符 =~.!~=~检验匹配是否成功:$result = $var =~ /abc/;若在该字符串中找到了该模式,则返回非零值,即true,不匹配则返回0,即false.!~则相反.这两个操作符适于条件控制中,如: 复制代码 代码如下:    if ($question =~ /please/

F#中DSL原型设计:语法检查和语义分析

最近,人们对于领域特定语言F#中DSL原型设计的兴趣卷土重来.这些语言不仅能够为特定领域提供更好等级的提炼,从而有助于减少在通用语言中因低等级构造而造成的错误:而且通过提供额外配置.定制的业务逻辑等,它们为用户提供了一种有效的机制,用于细调你的应用程序.总之,DSL能够让你的应用程序更加多样化并具有更好的伸缩性. 大致来讲,领域特定语言的工作方式有两种--你可以通过对以源DSL编写的源文本进行转译来实施,或者通过将源文本编译为可执行代码.这两种方式都有着独特的优点和缺点.对于解释器和编译器的实施

深入理解Javascript中的this关键字

 这篇文章主要介绍了深入理解Javascript中的this关键字,本文讲解了方法调用模式.函数调用模式.构造器调用模式.apply调用模式中this的不同之处,需要的朋友可以参考下     自从接触javascript以来,对this参数的理解一直是模棱两可.虽有过深入去理解,但却也总感觉是那种浮于表面,没有完全理清头绪. 但对于this参数,确实会让人产生很多误解.那么this参数到底是何方神圣? 理解this this是一个与执行上下文(execution context,也就是作用域)相