天天看點

了解F#中的模式比對與活動模式

模式比對(Pattern Matching)允許我們根據辨別符值的不同進行不同的運算,它通常被拿來跟C#中的if…else或switch文法結構相比較,結論往往是模式比對比後者要更為靈活、強大。那先來分析一下它靈活、強大在哪兒。

為什麼說模式比對是靈活、強大的?

在我前面寫過的幾篇随筆裡面,有幾次提到了模式比對,比如它能夠對簡單值(整數、字元串)比對,也可以對.NET類型進行比對,看下面兩個簡單的例子:

了解F#中的模式比對與活動模式
了解F#中的模式比對與活動模式

可以看到,這裡所用的模式比對沒有給人太多驚喜,不用費多大力氣就可以将其轉換為if…else或switch結構了。

先别急着離開,清單是FP中的典型資料結構,我們對它應用一下模式比對看看。

了解F#中的模式比對與活動模式
了解F#中的模式比對與活動模式

listOfList是一個清單的清單,兩個函數concatenateList和concatenateList2的功能都是将listOfList的元素連接配接為一個大的清單,隻不過一個用模式比對方式實作,一個使用if…then…else結構實作。可以看到concatenateList的代碼更為簡潔,但僅僅如此嗎?在concatenateList2中,我們按照傳統的看待連結清單(F#中的清單以連結清單實作)的方式,将其中的節點一個一個取出來進行處理,這種處理方式是較為具體和細節的;而在concatenateList中我們通過兩個簡單的模式“head :: tail”和“[]”就覆寫了清單的所有可能,可以說,我們找到了更好地分解清單這種資料結構的方式,進而可以更為通用地處理清單。

類似的,再來看看Union類型的情況。Union類型,有時稱為sum類型或discriminated union,可将一組具有不同含義或結構的資料組合在一起。它的一個典型應用是表示一顆樹:

了解F#中的模式比對與活動模式
了解F#中的模式比對與活動模式

這裡通過BinaryTree<'a>定義一個泛型二叉樹類型,printBinaryTreeValues函數用于列印其節點的值,這裡需要判斷節點的類型(子樹還是葉子),有趣的是,Leaf和Node自動抽象為“模式”,不需要任何額外的工作。這樣就可以看到一些所謂“靈活、強大”的影子了,對于Union類型所表示的資料結構,模式比對可以極為簡單、自然地分解、處理它。

除了清單和Union類型,元組對于模式比對的“自适應”也是類似的,這些已經夠我們解決很多問題了。那對于其它的更複雜的場景或者更特殊的領域,F#還有什麼大招呢?你一定能想得到,這就是活動模式。

活動模式(Active Pattern)

活動模式的思想就是把模式比對文法用于其他更多的資料結構。可以把它分為Single-Case、Multi-Case、Partial這幾種類型。我将逐一做出介紹。

Single-Case活動模式 

Single-Case是最簡單的活動模式形式,它将一個輸入值轉換為其它的值,比如:

了解F#中的模式比對與活動模式
了解F#中的模式比對與活動模式

這裡的UpperCase就是一個模式,它的類型資訊為:active recognizer UpperCase: string -> string,可以看到下面求result值的時候可以像前面一樣使用模式比對的文法了,UpperCase “FOO”可以了解為對于輸入值”foo”,應用了UpperCase模式後,結果應當為”FOO”,如果确實如此,那麼該模式就比對了,是以result的值為true。

UpperCase模式看起來像是一個函數,不過對于函數來說,沒法直接應用模式比對的文法。

Multi-Case活動模式

這裡(|Odd|Even|)就是Multi-Case模式了,Even的類型資訊為:active recognizer Even: int -> unit,即它沒有傳回值,是以在比對時,直接寫Even或Odd就可以了。

Partial活動模式 

簡單來說,Partial模式就是那些并不總是傳回值的模式。比如輸入值的範圍可能過于龐大,或者對于某些傳回值我們并不感興趣,可以将其忽略。比如,對于自然數來說,隻有一小部分是完全平方數或者能夠被7整除。

了解F#中的模式比對與活動模式
了解F#中的模式比對與活動模式

自然數有很多特性,而在函數describeNumber中,我們隻關注它是否是完全平方數或者7的倍數,其它的就都舍棄不管了。

應用 

我們來看看如何使用活動模式來操作XML文檔。

了解F#中的模式比對與活動模式
了解F#中的模式比對與活動模式

這裡首先定義針對XML節點的模式,然後應用該模式來遞歸列印出一個XML節點及其子節點的資訊。

可以看到使用活動模式,寥寥數語就可以描述出XML節點的通用資料結構來了,這為接下來對節點的操作提供了良好的基礎,而且我們回歸了問題本身——XML文檔,而不需要關注具體的程式設計細節。

小結 

這裡先是介紹了F#中模式比對的用法,這個可以了解為使用F#内置的模式,這樣我們就可以處理F#中的值和特定的資料結構,比如清單、Union類型和元組等;接下來更進一步,活動模式把模式比對的文法用到了其他更多的資料結構,這樣模式的應用範圍得到了很大的擴充。而且通過活動模式,我們可以将問題域轉換為一套術語來表達,進而脫離程式設計細節回歸到問題域本身,這也就有了一些LOP(Language-Oriented Programming)的特點,事實上,活動模式正是F#中LOP的實作方式之一。這個我将在後面的随筆做更深入的讨論。

參考 

本文轉自一個程式員的自省部落格園部落格,原文連結:http://www.cnblogs.com/anderslly/archive/2008/11/25/fsharp-adventure-pattern-matching-and-active-pattern.html,如需轉載請自行聯系原作者。