F#入門(mén):基本語(yǔ)法,模式匹配及List
F#隨著VSTS 2010 Beta1 發(fā)布也有一段時(shí)間了,園子里應(yīng)該也有不少人對(duì)它感興趣吧。下面的例子是我在學(xué)F# 基本語(yǔ)法時(shí)寫(xiě)的一個(gè)簡(jiǎn)單Sieve of Eratosthenes 實(shí)現(xiàn),通過(guò)剖析這一小段代碼,我希望大家能對(duì)F#有個(gè)簡(jiǎn)單認(rèn)識(shí),并能自己寫(xiě)一些簡(jiǎn)單的小程序。
F#入門(mén)代碼
- let GetAllPrimesBefore n =
- let container = Array.create (n+1) 0
- let rec loop acc = function
- |[] -> List.rev acc
- |hd::tl ->
- if container.[hd] =1 then
- loop acc tl
- else
- for j in [hd .. hd .. n] do
- container.[j] <- 1
- loop (hd::acc) tl
- loop [] [2 .. n]
- let primesBefore120 = GetAllPrimesBefore 120
廢話少說(shuō),直接進(jìn)入正題吧
- let GetAllPrimesBefore n =
第一行,申明函數(shù)GetAllPrimesBefore, 并且該函數(shù)有一個(gè)參數(shù)n, 在這里我沒(méi)有指定n的類(lèi)型,因?yàn)榫幚[器可以通過(guò)函數(shù)體對(duì)n的類(lèi)型進(jìn)去推斷,比如在本例中,n就是int類(lèi)型,當(dāng)然我們也可以顯示的指定n的類(lèi)型,比如 let GetAllPrimesBefore (n:int),這樣我們就指定了n為int型 (注意:(n:int)中的括號(hào)不能省略,let GetAllPrimesBefore n : int 的意思是該函數(shù)返回的值的int型)。說(shuō)完了參數(shù),再說(shuō)下返回值,同樣,編繹器會(huì)根據(jù)函數(shù)體上下文對(duì)返回值類(lèi)型進(jìn)去推斷,所以我們不需要申明返回類(lèi)型。
- let container = Array.create (n+1) 0
第二行,首先請(qǐng)注意該行與第一行相對(duì)有一個(gè)縮進(jìn)({TAB}),F#和Python一樣,也是通過(guò){TAB}縮進(jìn)來(lái)組織代碼結(jié)構(gòu)的。這一行我們定義了一個(gè)變量container,它的類(lèi)型是Array,大小為 n+1, 并且值全部初使化為0
- let rec loop acc = function
- |[] -> List.rev acc
- |hd::tl ->
- if container.[hd] =1 then
- loop acc tl
- else
- for j in [hd .. hd .. n] do
- container.[j] <- 1
- loop (hd::acc) tl
接下來(lái)就是這個(gè)函數(shù)的主要部分了(原程序中的3-11行),首先我們定義了一個(gè)遞歸函數(shù)(我們發(fā)現(xiàn)定義遞歸函數(shù)需要加rec關(guān)鍵字)。它接受兩個(gè)參數(shù),acc和一個(gè)List,有朋友可能要問(wèn)了,這里明明我只看到一個(gè)參數(shù)acc,你說(shuō)的那個(gè)List在哪呢?可能有細(xì)心的朋友也發(fā)現(xiàn)了這里的函數(shù)定義不光前面有rec,在等號(hào)后面還加了個(gè)function,那么function是做什么用的呢?
- let rec loop acc = function
F#入門(mén):模式匹配
這里我需要首先講一下Pattern Matching(模式匹配), Pattern Matching有些類(lèi)似于C#中的switch語(yǔ)句(當(dāng)然它要比C#中的switch強(qiáng)大許多,但這不是本文的目地,所以略去不表),可以根據(jù)expr的值去執(zhí)行某一具體分支,它的基本語(yǔ)法也很簡(jiǎn)單,我們還是結(jié)合一個(gè)具體實(shí)例來(lái)看一下(例子比較簡(jiǎn)單,只是為了說(shuō)明問(wèn)題)。 這個(gè)例子大家很容易看懂吧,我就不詳細(xì)解釋了,只是說(shuō)明一點(diǎn),'_'用來(lái)匹配所有別的情況。
- let ShowGreeting laguageInUse =
- match laguageInUse with
- | "C#" -> printfn "Hello, C# developer!"
- | "F#" -> printfn "Hello, F# developer!"
- |_ -> printfn "Hello, other developers!"
因?yàn)镻attern Matching在F#中的使用范圍實(shí)在太廣了,所以就引入了一種簡(jiǎn)化版,這就是上面大家看到的等號(hào)后面的function的作用,我們可以把上面的例子簡(jiǎn)化成
- let ShowGreeting = function
- | "C#" -> printfn "Hello, C# developer!"
- | "F#" -> printfn "Hello, F# developer!"
- |_ -> printfn "Hello, other developers!"
怎么樣?既少了給參數(shù)起名的煩惱,也少敲不少字吧,嘿嘿。
F#入門(mén):List基本類(lèi)型
接下來(lái)我再簡(jiǎn)單介紹下F#中非常重要的一個(gè)基本類(lèi)型List, 其基本表示形式為 [ item1;item2; .. ;itemn]
F#中List是immutable類(lèi)型,我們只能訪問(wèn)里面的值,不能改動(dòng)里面的值,任何改動(dòng)List的需求只能通過(guò)構(gòu)建新的List來(lái)實(shí)現(xiàn)。稍一思考,大家就會(huì)很快發(fā)現(xiàn)要實(shí)現(xiàn)一個(gè)高效的immutable list, 那最簡(jiǎn)單的就是對(duì)其頭結(jié)點(diǎn)進(jìn)去操作了(插入和刪除都可以達(dá)到O(1),當(dāng)然插入和刪除會(huì)構(gòu)建一個(gè)新的List,原List不會(huì)改變),F(xiàn)#中的List也是基于這種形式,所有的List都可以看成是Head+Tail(除了Head外的所有結(jié)點(diǎn)),F#提供了相應(yīng)的庫(kù)函數(shù)List.hd, List.tl,并且提供了:: (cons operator)來(lái)幫助我們方便的構(gòu)建一個(gè)List,比如1::2::[]就表示List [1;2] (注意1和2之間我用的是;不是, 如果寫(xiě)成[1,2],那個(gè)表示該List只有一個(gè)元素 (1,2),至于(1,2)是什么類(lèi)型,為了使文章盡量緊湊,我們今天就不講了)
有了上面這些知識(shí),再看本文一開(kāi)始的函數(shù)就簡(jiǎn)單多了
- let rec loop acc = function
- |[] -> List.rev acc
- |hd::tl ->
- if container.[hd] =1 then
- loop acc tl
- else
- for j in [hd .. hd .. n] do
- container.[j] <- 1
- loop (hd::acc) tl
首先,該函數(shù)的第二個(gè)參數(shù)是List,
當(dāng)List為空時(shí),就把a(bǔ)cc反序返回,
當(dāng)List不為空時(shí),把List分成兩部分(hd::tl),檢查當(dāng)當(dāng)前值n (n的值等于td) 是否己被標(biāo)記
如果己經(jīng)被標(biāo)記(container.[hd] =1),略過(guò)當(dāng)前值,檢查接下來(lái)的值 loop acc tl
如果沒(méi)有被標(biāo)記(當(dāng)前值是素?cái)?shù)),用當(dāng)前值和acc構(gòu)建一個(gè)新List (hd::acc),并對(duì)當(dāng)前值的所有倍數(shù)進(jìn)去標(biāo)記(for loop),然后檢查下一個(gè)值 loop (hd::acc) tl
這里有兩點(diǎn)需要特別說(shuō)明一下:
1. container是一個(gè)Array類(lèi)型的參數(shù),Array在F#中是mutable類(lèi)型的容器,我們可以修改里面的元素,訪問(wèn)元素用Array.[i], 修改元素用Array.<-[i] = newValue(不要忘記中間的.)
2. for loop的基本形式為 for <index> in <range> do, 我們可以使用[start .. end]或[start .. step .. end]來(lái)構(gòu)建一個(gè)range,當(dāng)然,這里的range其實(shí)也是一個(gè)List
看完了內(nèi)部函數(shù),我們?cè)俳又驴矗ㄔ绦虻?2行)
- loop [] [2 .. n]
這里就很簡(jiǎn)單了,調(diào)用我們剛剛定義的內(nèi)部函數(shù),(acc為空List [], 第二個(gè)參數(shù)為L(zhǎng)ist [2 .. n]),其返回值(List acc)就是函數(shù)GetAllPrimesBefore的返回值,F(xiàn)#中函數(shù)有返回值時(shí)不需要敲return.
函數(shù)調(diào)用也很簡(jiǎn)單,(不需要在參數(shù)與函數(shù)名之間加括號(hào))
- let primesBefore100 = GetAllPrimesBefore 100
后記
1. F#中函數(shù)體內(nèi)可以定義新的值,變量和函數(shù)。(只在當(dāng)前函數(shù)體內(nèi)可見(jiàn))。當(dāng)然,這樣做的好處顯而易見(jiàn),我就不啰嗦了。
2. Recursive function是functional programming中很常用的一種算法實(shí)現(xiàn)方式。functional programming language往往會(huì)針對(duì)尾遞歸進(jìn)行特別的優(yōu)化,F(xiàn)#也不例外,所以我們需要盡可能的把遞歸寫(xiě)成尾遞歸的形式,這個(gè)有時(shí)就需要像本文一樣借助accumulator來(lái)實(shí)現(xiàn)。
本文來(lái)自hiber的博客:《結(jié)合實(shí)例學(xué)習(xí)F#(一) --快速入門(mén)》。
【編輯推薦】