結合例項學習F#(二) --基本資料型別Discriminated Unions

iDotNetSpace發表於2009-08-14
題外話:
我寫這個主要是希望更多的.Net開發人員能瞭解F#,能在看到F#程式碼時不被一堆奇怪的符號搞暈(其實也沒幾個奇怪的符號).我沒有說過F#比別的語言好、會取代C#之類的話,只是希望更多的人能瞭解並開始使用F#(C#用的多了,瞭解下F#換換腦子也是挺不錯的)。寫的例子、程式碼都比較簡單,希望大家多多包涵。可能有朋友手頭沒有VSTS 2010 Beta1,這個沒有關係,因為F#還有一個為VSTS 2008準備的獨立安裝包,大家可以在這裡下載安裝它。

上節我們通過一個簡單的例子瞭解了怎樣在F#中宣告變數,定義函式,並且用到了F#中兩個重要的資料型別List和Array,今天我主要介紹F#中一個非常重要的immutable資料型別Discriminated Unions。還是首先看一個例子,這是我寫的一個簡單的生成二分查詢樹的例子。
 1 type Tree<'a> = 
 2     | Node of 'a * Tree<'a> * Tree<'a>
 3     | Nil 
 4 
 5 let generateBinarySearchTree l = 
 6     let rec insert a = function   
 7         | Node(root,left,right) when a -> Node(root, (insert a left), right)
 8         | Node(root,left,right) when a > root   -> Node (root,left, (insert a right))             
 9         | Nil -> Node(a, Nil,Nil)        
10             
11     let rec loop acc = function
12         |[] -> acc
13         |hd::tl -> loop (insert hd acc) tl
14     
15     loop Nil l
16 
17 let tree1 = generateBinarySearchTree [5;3;9;4;6;7]

我們首先來看前三行,沒錯,這就是今天要重點介紹的Discriminator Union
type Tree<'a> = 
    | Node of 'a * Tree<'a> * Tree<'a>
    | Nil 
首先注意到我們這次使用的是type,而不是前面常用的let關鍵字。 F#中使用type關鍵字來定義使用者自定義型別,在這裡我們定義了一個型別Tree, 那麼Tree後面的又是啥意思呢?可能有的朋友己經猜到了,它表示a是一個泛型佔位符,在實際使用中,a可能是int型,也可能是string等等(注意別忘了a前面的單引號)。後面二行就是具體的Tree定義了,它表示我們定義的Tree有兩種可能,有可能是Node,也有可能是Nil。 我們先來看第一種情形
Node of 'a * Tree * Tree 
它表示Node的型別是 'a * Tree * Tree, 那麼這個又表示什麼呢?其實它是F#中另外一種重要的immutable型別Tuple, Tuple很容易理解,它表示把一個資料集合在邏輯上看作是一個整體。看個例子大家就明白了(注意分隔符是逗號)
let s2 = (1,"hello")

(在這裡我們定義了一個型別為int * string的Tuple. 要使用它裡面的值也很簡單,我們可以宣告新的變數並用s的值來初始化它們。 let i,s = s2就表示我們宣告瞭int型變數i,它的值為1, string型變數s,它的值是2)

回到我們的例子中來, 'a * Tree * Tree 就很容易理解了,因為在定義Discriminated union時可以遞迴引用自己。

Tree的第二種情形Nil很簡單,它表示一個什麼都沒有的空結點.

通過我上面詳細的解釋,我想大家也明白了什麼是Discriminated union, 它表示一組有限的可選情形,並且每種情形都有自己的嚴格定義。回到我們上面的例子,Tree有兩種情形,要麼是 'a * Tree * Tree的Node,要麼是一個空的Nil。大家也看到了它和Pattern matching結合使用非常頻繁,這下明白為什麼叫Discriminated union了吧

如果你認真讀到上一篇文章的話,接下來構建二分查詢樹的程式碼比較簡單,我就不解釋了。我們接下來看如何判斷某一個值是否在一個構建好的二分查詢樹中。

let rec tryFind x = function
    
| Node(root,_,_) when x = root -> Some(x)
    
| Node(root,left,_) when x-> tryFind x left
    
| Node(root,_,right) when x > root -> tryFind x right
    
| _ -> None 
 首先要注意我使用了五個'_',前面四個看起來好象和最後的一個有些不一樣。記得我在上一篇中說過'_'用在Pattern Matching中用來匹配所有別的情況,而且我說過F#裡的Pattern Matching要比C#中的Switch強大,在這裡我們就看到了它的強大之處,它可以在找到匹配後,為匹配的各部分繫結一個變數名來方便我們後面的呼叫,在繫結時如果我們僅僅對某些部分感興趣,那麼我們就可以使用'_'來代替我們不感興趣的部分(注意'_'只能繫結一個對應部分,要對應兩個我們就要敲兩個'_','_').
  其次我們注意到tryFind的返回值好象有兩種情況呀,Some(x)和None,一個函式怎麼能返回兩種不同型別的值呢? 呵呵,忘記我們今天主要在講Discriminated union了?這是個F#裡事先定義好的一個discriminated union,它有自己的名字叫Option,它的定義非常簡單,有了前面的基礎,這個就不需要我解釋了吧。 
type option<'a> = 
    |Some of 'a
    |None
   
總結:今天我主要說了F#中非常重要的一種immutable類型Discriminated union,並順帶說了下另兩個簡單的型別Tuple和Option。簡單的functional programming知識就剩下最重要function沒有說了,下一篇我主要來說說F#裡的函式,希望在下一篇後,大家不再覺得F#難懂難用了。
 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-612297/,如需轉載,請註明出處,否則將追究法律責任。

相關文章