F#是微軟.NET開發平台的一門程式設計語言,其最大的特點是對函數式程式設計(FP,FunctionalProgramming)的引入;F#對面向對象(OOP)程式設計的支援也很出色,使用F#語言,開發人員可以自由選擇函數式程式設計或面向對象程式設計來實作他們
的項目。此外,F#還可以與.NET平台上C#、VB等其他程式設計語言緊密結合。CPU多核心化和雲計算的背景下,函數式程式設計可以很好的解決多并發運算的問
題(在處理并發問題方面,面向對象程式設計存在一定程度的固有缺陷,比如類和執行個體化過程中産生的一些副作用,詳細請參考51CTO.com對另一門函數式程式設計
語言Erlang的視訊訪談《
因并發而生 因雲計算而熱:Erlang專家訪談實錄》)。微軟看到了這個趨勢,試圖通過專門為函數式程式設計打造的F#語言提升.NET平台在并發處理、多核多并發方面的能力,進一步提升開發人員的生産力和代碼運作效率。
在2009年的TechED上,51CTO.com就F#和函數式程式設計的問題視訊采訪了微軟MVP趙颉老師,我們可以采訪視訊了解F#和函數式程式設計最近的發展,詳細請參考《TechED 09視訊專訪:F#與函數式程式設計語言》。
F#小背景:看似年輕的F#已經有近10年的曆史。最初由微軟研究院的Don Syme于2002年立項研發;F#在2005年推出第一個版本,2007年底,微軟宣布F#進入産品化階段。在不斷的改進中,F#從C#、Linq和Haskell中吸收了很多優點。
F#程式設計起步
F#可以運作在.NET Framework 2.0版本以上的平台。如果你的Visual Studio之前沒有安裝F#,可以從微軟F# Developer Center獲得(http://msdn.microsoft.com/en-us/fsharp/default.aspx)。
不能免俗,讓我們來看看F#的Hello World代碼:
1.
2. #light
3. System.Console.WriteLine(“This is one hello”)
4. printfn “This is another hello”
5.
将代碼儲存為hello.fs檔案後,我們需要在指令行中通過fsc.exe編譯生成一個.NET程式集。在指令行中的編譯代碼如下:
1. fsc hello.fs
通過上面的代碼,我們就得到了常見的可執行檔案(.exe檔案),這就是我們F#的起步——hello.exe。
F#小提示:F#是.NET平台上的一個編譯型語言,但仍然可以像腳本語言一樣運作。可以使用Visual Studio或fsi.exe(在F#安裝目錄下的bin目錄)進行F#腳本的執行。
代碼解讀
讓我們來仔細看看hello.fs檔案裡的代碼
◆程式首先以“#light”開始,在以後的F#之路上,我們會經常看到“#light”;大多數時候,“#light”總是出現在F#程式的開始位置,這是F#輕量級文法的辨別;在最新的F#版本中,#light将作為預設選項。
◆“System.Console.WriteLine”調用一個.NET基礎類(熟悉C#或VB.NET的朋友會相當熟悉)用來初始化一些必要的功能。
◆“printfn”是F#的一個常用函數,他會将雙引号中的參數輸出到控制台上顯示。
跟其他程式的Hello World一樣,這段F#代碼簡單易懂,看着跟其他語言寫就的Hello World還有些相似;但作為函數式程式設計語言,F#的文法和程式設計中的思路卻有很大的不同。在下周的章節中,我們将深入F#程式設計,講解F#的類型系統及編譯機制。
在上一篇教程《F#與函數式程式設計概述》中我們了解到F#和函數式程式設計的一些特點,更多關于F#語言和函數式程式設計的介紹可以參考51CTO之前對微軟MVP趙颉老師的專訪《TechED 09視訊專訪:F#與函數式程式設計語言》。本節教程我們将學習到F#的一些基礎原理,在開始之前,讓我們先溫習一下我們的Hello World代碼:
1.
2. #light
3.
System.Console.WriteLine(“This is one hello”)
4.
printfn “This is another hello”
5.
F#是函數式和面向對象的混合體。它有時候會看起來與C#或Visual Basic驚人的相似,但卻又完全陌生。F#程式以一系列的表達式形式組成,每個表達式可以通過“let”辨別符被指定,比如:
1.
2. let fles = System.IO.DirectoryInfo(@”C:\Users\Chance”).
3. GetFiles()
4.
在上面的代碼中,“fles”被指定了一個值,在這個例子中,是一個檔案路徑。有意思的是,程式運作中,直到語句在得到右側的傳回值前,“fles”的實際類型都沒有被詳細定義。你可能覺得有些别扭,在Java或其他程式設計語言中,變量fles應該被定義成一種資料類型,string或
是其他什麼類型以在記憶體中可以明确的被編譯器差別對待,但這些規則在F#中有些不同。這也導緻我們的F#簡明教程稍有不同,我們不會像通常的教程那樣介紹
F#的基本資料類型,從某種意義上說,F#可以是任意類型或隻有一個類型。
F#小提示:F#是一種類型推斷語言,它們在編譯過程中被推斷和确定。如果你在VisualStudio中編寫F#,将滑鼠指向某個值就會得到它的類型,編譯器可以通過函數體或其他方式的定義推斷出類型;Visual
Studio是開發F#的主要工具,51CTO推薦您閱讀
Visual Studio 2010中關于F#的資源一文。
類型推斷(Type Inference)
我們說資料的類型是被推斷出的,因為F#的編譯期程序會試圖根據變量自身的特點來判斷出它的類型并確定這種類型是安全的。盡管F#是強類型語言,但變量的類型聲明在類型的判斷推理過程中并不是必須的。
類型推斷有自身的優點。在使用F#開發一些大型應用時,比如.NET和Java開發者都很熟悉的泛型特性(Generics)便是由類型推斷來完成。注意,F#編譯器會視任何沒有類型标注的表達式為泛型。例如,下面的函數中,各變量的類型被定義(推斷為)泛型,即使程式編寫者沒有定義任何類型。
1.
2. let f x =
3. let y = g x
4. h y
5.
1.
2. let f (x:’a) : ’b =
3. let y:’c = (g:’a->’c) x
4. (h:’c->’b) y
5.
F#小提示:在F#中,泛型類型參數是一個以撇号為字首的字元。比如上面例子中的’b和’c就是最常用的泛型參數。像在.NET中一樣,泛型類型也可使用尖括号文法,比如“Dictionary<’Key,’Value>”。隻有一個泛型參數的時候,你有時候會看到它使用
‘字首’文法而不是尖括号——最常見的是和F#泛型類‘list’和‘option’一起使用。比如“int
list”和“list<int>”表達同一種功能,隻是書寫方式不同。
F#類型推斷機制
F#語言中的大多數類型推斷可以遵循以下兩條規則。首先,如果一個函數用于産生一個值,編譯器将假定該值的類型是函數需求的。第二,如果一個值是一個表達式的必然結果,這個值的類型是這個表達式所決定的。
有些情況下這些簡單的規則不夠完全,編譯器必須需要類型聲明。比如,當一個算數運算符被使用,F#會處理的非常謹慎,如果沒有程式員的明确代碼,不會将一個數值型賦予另一個。這樣做是為了確定F#在進行大規模數值計算時,類型推斷不會加重編譯器的負擔。
針對第二條規則的例子在方法過載的情況下發生。比如Write方法在System.Console(.NET中System.Console封裝了基于控制台應用程式的輸入、輸出和錯誤流操作)中有18個負載。類型推斷可以确定傳送給它的類型,但是無法确定另一個方向傳送的值的類型。
類型推斷不隻是簡單的符号,它還可以用于程式功能的檢測。當你寫了一段代碼,類型推斷功能為這些代碼智能的獲得了指定的類型,這意味着錯誤不會被引入程式。這種機制使F#獲得動态語言的代碼簡潔性的同時保證了完全靜态的類型系統。
更多關于F#的類型和文法基礎請參考:
◆ F#資料類型:Discriminator Union
◆ F#基本文法,模式比對及List
F#的類型系統和類型推斷機制是學習和了解F#語言的基礎,掌握了這些有利于我們之後的學習。下周我們将繼續F#的學習,一起探究F#的基礎文法。
在上一節F#教程中,我們對F#的類型系統和類型推斷機制有了一個初步的認識。F#的類型推斷原理是學習F#的重要基礎。本節課程,我們将在F#類型基礎上進一步學習F#的一些基本文法。
“let”表達式是F#文法的核心,可以用作定義函數、序列等多種用途。另外,F#使用空格來标記程式塊的開始與結束。
定義值
1. let x = 2
定義函數值
1. let f a = a + x
定義循環函數
1.
2. open System.IO
3. let rec printSubDirFiles dir =
4. let fles = Directory.GetFiles dir
5. let dirs = Directory.GetDirectories dir
6. printf “%s\n%A\n\n” dir fles
7. Array.iter printSubDirFiles dirs
8.
此外,F#還提供傳統的循環和疊代等流程控制結構,比如if、for、while。但我們需要注意的是,F#中的“if…then”和“if…then…else”與傳統的面向對象語言有些不同。在F#中,大多數表達式必須含有一個值,并且控制結構“if…then…else”表達式的
兩邊的值必須是同一類型。注:F#的這種文法約定源自其推斷型語言的編譯機制,詳細請參考上一節教程中關于
F#類型推斷機制的介紹。
F#中的常用流程控制語句示例

與大多數.NET平台上的程式設計語言相似,F#也提供一些組織代碼的機制。事實上,F#提供子產品和命名空間兩種方式,下面的一些示範将給出C#和VB的命名空間。F#的子產品化不隻局限與文法範圍,還提供子產品化的層級标準,例如集合和函數。
F#的基礎代碼組織:命名空間、類型和模型
1.
2. namespace MyFSharpProg
3. open System.Net
4. type Foo () =
5. member x.GetRequest = WebRequest.
6. Create
7. module Main = begin
8.
// values and functions here
9. end
10.
與傳統的函數式程式設計原則相同,多數時候,F#的辨別符是不可變的。但F#允許定義和修改使用“mutable”保留字的值,或通過“ref”保留字改變其前面的引用。mutable的值可以通過左箭頭操作(“<-”);ref的值可以通過“:=”操作符制指定。我們可以通過“!”擷取ref的
值。下面來看具體示例:
聲明/更新可變值
1.
2. let mutable x = 0
3. x <- x + 1
4.
聲明/更新參考值
1.
2. let x = ref 0
3. x := !x + 1
4.
F#小提示:在習慣了C#或Java等程式設計語言後,剛剛開始F#程式設計,閱讀F#代碼感覺就像亂碼一樣。因為F#為了保有函數式程式設計的一些優秀特質,不得不引入一些如“<-”、“:=”、“!”等奇怪的符号作為操作符或運算符;另外,F#在代碼中需要通過一些
推斷機制來評判變量的類型,在閱讀F#代碼時,應對F#的類型系統做到心中有數,是以,多數時候我們看到的是“let”,而不是傳統的“int”、
“string”、“float”等。希望大家能充分了解
F#的類型系統和類型推斷機制,這是F#的重要基礎,也是走進函數式程式設計語言的重要一步。