F 學習之路 3 如何組織程式 下

2022-03-05 22:43:40 字數 3767 閱讀 6958

二、命名空間(namespace)

命名空間,將一組邏輯上相關的型別、模組放在一起,主要是為了解決名稱衝突的問題,同時也便於更好的理解程式結構。f#的命名空間概念及定義與c#基本相似。

1、定義命名空間

在f#中定義命名空間,使用關鍵字namespace。

namespace fsharplearning

命名空間宣告的位置應在原始檔的開頭,除注釋、部分指令外,namespace宣告前不能有語句或表示式。     

命名空間的作用域為定義開始到檔案結束,除非遇到下乙個命名空間的定義。在f#中乙個源**檔案中可以定義多個命名空間。命名空間沒有巢狀的概念,對f#來說,命名空間只是為了給命名空間下的元素定義乙個完全限定名稱而已。

c#可以在乙個命名空間中巢狀定義命名空間,而f#不支援這種定義方式。

//在c#中定義巢狀命名空間的偽碼 

namespace

n1  

} } 

這在c#中定義了乙個n1.n2的命名空間。

不過f#支援使用.號分隔(與c#一致),表示一種邏輯上的層次關係,值得說明的是,對於編譯器來說命名空間只是為了生成長名稱(long name)而已,並沒有真正的層次關係。

namespace fsharplearning.stringextensions

命名空間可以使用open關鍵字開啟。在f#中,open的使用與c#的using也有不同。在c#中using語句後跟的命名空間必須是全名稱,但在f#中不僅支援這種寫法,還有另一種使用方法。

open fsharplearning

open stringextensions(*open fsharplearning.stringextensions*)

在f#中雖然沒有巢狀定義命名空間的語法,但卻能使用open開啟邏輯上巢狀的命名空間。

由於使用了open關鍵字,使用未限定名訪問,所以出現了同名的標識,f#會以最後乙個open開啟的命名空間及模組的名稱,同名的會隱藏。後者隱藏前者。你當然可以使用完全限定名來訪問隱藏的名稱,其次你也可能使用別名的方式訪問。  

#light

module

sw=system.windows.forms 

module

se=fsharplearning.stringextensions 

open

se lets="

test

".link 

sw.messagebox.show(

"good")

|>ignore 

2、定義命名空間下的子模組

#light  

namespace

fsharplearning 

module

datetimeextensions = 

open

math 

type

system.datetime 

with

member

x.tonumber = 

bignum.of_bigint << bigint.fromint64 <

|x.ticks  

你注意到不同了嗎?子模組的定義於頂級模組不同在於,子模組的名稱後面接著乙個等號(=)。

module submodulename =begin

(*模組中可以定義子模組、型別、函式、值*)

end

begin end在#light指令下可以省略。

子模組與頂級模組從概念上來講,子模組是指巢狀在模組之下的模組。頂級模組在乙個命名空間下只可以定義乙個,並且必須是命名空間下第一條語句。

子模組卻可以定義多個。乙個命名空間下可以定義多個子模組。

3、命名空間、模組之間的區別

命名空間主要用於組織邏輯上相關的型別。而模組主要用於定義對乙個型別相關的行為。

命名空間就是為一組相關的型別定義乙個完全限定名稱,一是從邏輯上表示相關,二是為了解決名稱衝突

模組則是為了模組化**。在函式式語言中,通常把對某乙個型別的操作放入到乙個模組中,主要為了表示邏輯上依附於某乙個型別,也部分解決名稱衝突

從語義上來講命名空間與模組沒有什麼不同,都是為了解決名稱衝突與模組化程式設計而引入的語法。歷史上來看,命名空間是後引入的,模組早於命名空間出現。

三、再議f#如何編譯及確定入口點

然後知道這一些仍然不夠,很模糊,也並不完全正確。下面我用c#概念來描述一下規則

f#在編譯的過程中,與源**檔案有很大關係。根據每乙個源**檔案,f#會生成乙個程式集訪問級別的靜態類(internal static),為便於描述,我把這個靜態類稱為內部靜態類。每乙個模組生成乙個靜態類。為了初始化這些靜態類,f#會為每個靜態類生成乙個靜態建構函式。內部靜態類會初始化整個源**檔案中所有的靜態成員。換句話如果乙個源**檔案中有多個模組,那麼這些模組都會被初始化。

f#以傳遞他的最後乙個檔名作為程式入口點,會生成乙個_main的方法。這是生成可執行檔案預設的規則。顯然生成dll,就沒有入口點的說法,也就不會生成這個_main方法了。

不過f#還允許你指定入口方法。

#light

[>]

lettest (a:

string

) = 

printfn 

"hello world

"system.console.readkey(

true) |

>ignore 

使用entrypointattribute屬性指定乙個方法為程式入口點,這種定義方法與c#很相似。不過,這個函式必須位於你傳遞給編譯器最後乙個檔案中,且必須位於檔案的最後。

對於可執行檔案來說,程式執行時,入口點所在的源**檔案(編譯器為我們生成的內部靜態類)會首先載入,如果這個內部靜態類引用了其他源**檔案,那麼就會載入那個引用的源**檔案的內部靜態類,如果引用的是型別,會載入相關型別,如果是模組,則會執行模組的靜態建構函式,這種方式稱為按需載入原則。對於dll,由於沒有入口點,所以程式集載入時不會強制執行靜態初始化,只會發生在呼叫時,當引用某個型別或模組時,相應的型別或模組所在的源**檔案的內部靜態類靜態構造器會被執行。

四、總結:

1、命名空間、模組的命名應以大寫字母開頭。

2、對於模組不要使用open,但使用可選型別擴充套件的模組除外,可選型別擴充套件的模組應使用型別名加上extensions。

命名空間下主要包含型別及模組,型別和模組都有很好的封裝能力,因此大多數情況下不會發生名稱衝突的問題。而使用open開啟了模組,就增大了名稱衝突,並且也會為閱讀**造成影響,前面說過模組主要是為封裝某一型別的行為或某一組邏輯上相同的行為,從某種意義上說,是模擬oo中的類的職責。f#庫的list,map,seq這些模組如果使用open開啟,就會造成閱讀上的困難,也會造成名稱隱藏。

但可選型別擴充套件則可以使用open開啟。

type system.datetime with

member x.tonumber =

bignum.of_bigint << bigint.fromint64 <| x.ticks 

可選型別擴充套件,與c#3.0的擴充套件方法相似,可以擴充套件某一型別的功能。可選型別擴充套件,必須定義在模組中,且擴充套件的型別名必須使用完全限定名稱。

3、源**檔名要使用字母開頭。

4、不要輕易使用open的未限定名稱方法,這很難看出完整的命名空間。

5、頂級模組不與命名空間一同使用,命名空間與子模組一起使用。 

6、不要把不相關的模組放到乙個源**檔案中,即使是同乙個命名空間下的一組相關的模組

f#在生成內部靜態類時是以源**檔案為單位的,不管呼叫了哪個模組中的值、型別、函式,任何乙個在這個源**檔案中定義的模組,都會被靜態初始化。

如何組織編寫模板程式

前言 常遇到詢問使用模板到底是否容易的問題,我的回答是 模板的使用是容易的,但組織編寫卻不容易 看看我們幾乎每天都能遇到的模板類吧,如stl,atl,wtl,以及boost的模板類,都能體會到這樣的滋味 介面簡單,操作複雜。我在5年前開始使用模板,那時我看到了mfc的容器類。直到去年我還沒有必要自己...

程式學習之路

大學畢業已經快半年了,目前從事的工作讓我覺得特沒意思,大學所學的知識根本就用不上,也不需要那麼高深的知識,給我感覺這裡的工作簡單粗暴,體力輸出比較多。生活缺乏激情,我認為是件很糟糕的事情。我正值奮鬥之年,然而工作讓我缺乏激情,失去追求夢想的慾望,慢慢的墮落。每天上班回來不是打球就是玩手機,或者是看電...

Linux學習之路(3)

linux 系統目錄結構 登入系統後,在當前命令視窗下輸入命令 ls 樹狀目錄結構 以下是對這些目錄的解釋 在linux系統中,有幾個目錄是比較重要的,平時需要注意不要誤刪除或者隨意更改內部檔案。etc 上邊也提到了,這個是系統中的配置檔案,如果你更改了該目錄下的某個檔案可能會導致系統不能啟動。bi...