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

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

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


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

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

 [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,可将一组具有不同含义或结构的数据组合在一起。它的一个典型应用是表示一颗树:

))))


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

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

活动模式(Active Pattern)

活动模式的思想就是把模式匹配语法用于其他更多的数据结构。可以把它分为Single-Case、Multi-Case、Partial这几种类型。我将逐一做出介绍。

Single-Case活动模式

Single-Case是最简单的活动模式形式,它将一个输入值转换为其它的值,比如:

 -> true


这里的UpperCase就是一个模式,它的类型信息为:active recognizer UpperCase: string -> string,可以看到下面求result值的时候可以像前面一样使用模式匹配的语法了,UpperCase “FOO”可以理解为对于输入值”foo”,应用了UpperCase模式后,结果应当为”FOO”,如果确实如此,那么该模式就匹配了,所以result的值为true。

UpperCase模式看起来像是一个函数,不过对于函数来说,没法直接应用模式匹配的语法。

Multi-Case活动模式

 -> false


这里(|Odd|Even|)就是Multi-Case模式了,Even的类型信息为:active recognizer Even: int -> unit,即它没有返回值,所以在匹配时,直接写Even或Odd就可以了。

Partial活动模式

简单来说,Partial模式就是那些并不总是返回值的模式。比如输入值的范围可能过于庞大,或者对于某些返回值我们并不感兴趣,可以将其忽略。比如,对于自然数来说,只有一小部分是完全平方数或者能够被7整除。

 x looks normal.


自然数有很多特性,而在函数describeNumber中,我们只关注它是否是完全平方数或者7的倍数,其它的就都舍弃不管了。

应用

我们来看看如何使用活动模式来操作XML文档。

    temp.LoadXml(text)
temp

printXml (doc.DocumentElement :> System.Xml.XmlNode)


这里首先定义针对XML节点的模式,然后应用该模式来递归打印出一个XML节点及其子节点的信息。

可以看到使用活动模式,寥寥数语就可以描述出XML节点的通用数据结构来了,这为接下来对节点的操作提供了良好的基础,而且我们回归了问题本身——XML文档,而不需要关注具体的编程细节。

小结

这里先是介绍了F#中模式匹配的用法,这个可以理解为使用F#内置的模式,这样我们就可以处理F#中的值和特定的数据结构,比如列表、Union类型和元组等;接下来更进一步,活动模式把模式匹配的语法用到了其他更多的数据结构,这样模式的应用范围得到了很大的扩展。而且通过活动模式,我们可以将问题域转换为一套术语来表达,从而脱离编程细节回归到问题域本身,这也就有了一些LOP(Language-Oriented Programming)的特点,事实上,活动模式正是F#中LOP的实现方式之一。这个我将在后面的随笔做更深入的讨论。

(要了解本人所写的其它F#随笔请查看 F#系列随笔索引

参考
超越F#基础——活动模式 by Robert Pickering
Introduction to F# Active Patterns by Chris Smith

相关文章:

  • 2021-10-01
  • 2021-06-08
  • 2022-12-23
  • 2021-08-26
猜你喜欢
  • 2022-02-19
  • 2021-12-09
  • 2022-12-23
  • 2021-11-22
  • 2021-05-26
  • 2021-09-29
相关资源
相似解决方案