FSharp簡明教程_第1頁
FSharp簡明教程_第2頁
FSharp簡明教程_第3頁
FSharp簡明教程_第4頁
FSharp簡明教程_第5頁
已閱讀5頁,還剩10頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、F#簡明教程本文轉載來自: 1. F#與函數式編程概述F#是微軟.NET平臺上一門新興的函數式編程語言,通過函數式語言,開發人員可以輕松應對多核多并發時代的并行計算和分布問題。本文是F#簡明教程的第一章,帶您走進F#和函數式編程。F#是微軟.NET開發平臺的一門編程語言,其最大的特點是對函數式編程(FP,Functional Programming)的引入;F#對面向對象(OOP)編程的支持也很出色,使用F#語言,開發人員可以自由選擇函數式編程或面向對象編程來實現他們的項目。此外,F#還可以與.NET平臺上C#、VB等其他編程語言緊密結合。CPU多核心化和云計算的背景下,函數式編程可以很好的解

2、決多并發運算的問題(在處理并發問題方面,面向對象編程存在一定程度的固有缺陷,比如類和實例化過程中產生的一些副作用,詳細請參考51CTO.com對另一門函數式編程語言Erlang的視頻訪談因并發而生 因云計算而熱:Erlang專家訪談實錄)。微軟看到了這個趨勢,試圖通過專門為函數式編程打造的F#語言提升.NET平臺在并發處理、多核多并發方面的能力,進一步提升開發人員的生產力和代碼運行效率。在2009年的TechED上,51CTO.com就F#和函數式編程的問題視頻采訪了微軟MVP趙頡老師,我們可以采訪視頻了解F#和函數式編程最近的發展,詳細請參考TechED 09視頻專訪:F#與函數式編程語言。

3、F#小背景:看似年輕的F#已經有近10年的歷史。最初由微軟研究院的Don Syme于2002年立項研發;F#在2005年推出第一個版本,2007年底,微軟宣布F#進入產品化階段。在不斷的改進中,F#從C#、Linq和Haskell中吸收了很多優點。1.1 F#編程起步F#可以運行在.NET Framework 2.0版本以上的平臺。如果你的Visual Studio之前沒有安裝F#,可以從微軟F# Developer Center獲得()。不能免俗,讓我們來看看F#的Hello World代碼:1 #light 2 System.Console.WriteLine(“This is one h

4、ello”) 3 printfn “This is another hello” 將代碼保存為hello.fs文件后,我們需要在命令行中通過fsc.exe編譯生成一個.NET程序集。在命令行中的編譯代碼如下:fsc hello.fs 通過上面的代碼,我們就得到了常見的可執行文件(.exe文件),這就是我們F#的起步hello.exe。F#小提示:F#是.NET平臺上的一個編譯型語言,但仍然可以像腳本語言一樣運行。可以使用Visual Studio或fsi.exe(在F#安裝目錄下的bin目錄)進行F#腳本的執行。1.2代碼解讀讓我們來仔細看看hello.fs文件里的代碼 程序首先以“#ligh

5、t”開始,在以后的F#之路上,我們會經常看到“#light”;大多數時候,“#light”總是出現在F#程序的開始位置,這是F#輕量級語法的標識;在最新的F#版本中,#light將作為默認選項。 “System.Console.WriteLine”調用一個.NET基礎類(熟悉C#或VB.NET的朋友會相當熟悉)用來初始化一些必要的功能。 “printfn”是F#的一個常用函數,他會將雙引號中的參數輸出到控制臺上顯示。跟其他程序的Hello World一樣,這段F#代碼簡單易懂,看著跟其他語言寫就的Hello World還有些相似;但作為函數式編程語言,F#的語法和編程中的思路卻有很大的不同。在

6、下周的章節中,我們將深入F#編程,講解F#的類型系統及編譯機制。2. F#類型系統和類型推斷機制F#是一種類型推斷語言,它們的類型在編譯過程中被推斷和確定;這與Java或C#中的泛型基本相似,本節教程我們將介紹F#的類型系統和類型推斷機制,這是理解F#的基礎。在上一篇教程F#與函數式編程概述中我們了解到F#和函數式編程的一些特點,更多關于F#語言和函數式編程的介紹可以參考51CTO之前對微軟MVP趙頡老師的專訪TechED 09視頻專訪:F#與函數式編程語言。本節教程我們將學習到F#的一些基礎原理,在開始之前,讓我們先溫習一下我們的Hello World代碼:1 #light 2 System

7、.Console.WriteLine(“This is one hello”) 3 printfn “This is another hello” F#是函數式和面向對象的混合體。它有時候會看起來與C#或Visual Basic驚人的相似,但卻又完全陌生。F#程序以一系列的表達式形式組成,每個表達式可以通過“let”標識符被指定,比如:1 let fles = System.IO.DirectoryInfo(”C:UsersChance”). 2 GetFiles() 在上面的代碼中,“fles”被指定了一個值,在這個例子中,是一個文件路徑。有意思的是,程序運行中,直到語句在得到右側的返回值前

8、,“fles”的實際類型都沒有被詳細定義。你可能覺得有些別扭,在Java或其他編程語言中,變量fles應該被定義成一種數據類型,string或是其他什么類型以在內存中可以明確的被編譯器區別對待,但這些規則在F#中有些不同。這也導致我們的F#簡明教程稍有不同,我們不會像通常的教程那樣介紹F#的基本數據類型,從某種意義上說,F#可以是任意類型或只有一個類型。F#小提示:F#是一種類型推斷語言,它們在編譯過程中被推斷和確定。如果你在Visual Studio中編寫F#,將鼠標指向某個值就會得到它的類型,編譯器可以通過函數體或其他方式的定義推斷出類型;Visual Studio是開發F#的主要工具,5

9、1CTO推薦您閱讀Visual Studio 2010中關于F#的資源一文。2.1 類型推斷(Type Inference)我們說數據的類型是被推斷出的,因為F#的編譯期進程會試圖根據變量自身的特點來判斷出它的類型并確保這種類型是安全的。盡管F#是強類型語言,但變量的類型聲明在類型的判斷推理過程中并不是必須的。類型推斷有自身的優點。在使用F#開發一些大型應用時,比如.NET和Java開發者都很熟悉的泛型特性(Generics)便是由類型推斷來完成。注意,F#編譯器會視任何沒有類型標注的表達式為泛型。例如,下面的函數中,各變量的類型被定義(推斷為)泛型,即使程序編寫者沒有定義任何類型。1 let

10、 f x = 2 let y = g x 3 h y 1 let f (x:a) : b = 2 let y:c = (g:a->c) x 3 (h:c->b) y F#小提示:在F#中,泛型類型參數是一個以撇號為前綴的字符。比如上面例子中的b和c就是最常用的泛型參數。像在.NET中一樣,泛型類型也可使用尖括號語法,比如“Dictionary<Key,Value>”。只有一個泛型參數的時候,你有時候會看到它使用前綴語法而不是尖括號最常見的是和F#泛型類list和option一起使用。比如“int list”和“list<int>”表達同一種功能,只是書寫方式

11、不同。2.2 F#類型推斷機制F#語言中的大多數類型推斷可以遵循以下兩條規則。首先,如果一個函數用于產生一個值,編譯器將假定該值的類型是函數需求的。第二,如果一個值是一個表達式的必然結果,這個值的類型是這個表達式所決定的。有些情況下這些簡單的規則不夠完全,編譯器必須需要類型聲明。比如,當一個算數運算符被使用,F#會處理的非常謹慎,如果沒有程序員的明確代碼,不會將一個數值型賦予另一個。這樣做是為了確保F#在進行大規模數值計算時,類型推斷不會加重編譯器的負擔。針對第二條規則的例子在方法過載的情況下發生。比如Write方法在System.Console(.NET中System.Console封裝了基

12、于控制臺應用程序的輸入、輸出和錯誤流操作)中有18個負載。類型推斷可以確定傳送給它的類型,但是無法確定另一個方向傳送的值的類型。類型推斷不只是簡單的符號,它還可以用于程序功能的檢測。當你寫了一段代碼,類型推斷功能為這些代碼智能的獲得了指定的類型,這意味著錯誤不會被引入程序。這種機制使F#獲得動態語言的代碼簡潔性的同時保證了完全靜態的類型系統。更多關于F#的類型和語法基礎請參考: F#數據類型:Discriminator Union F#基本語法,模式匹配及ListF#的類型系統和類型推斷機制是學習和理解F#語言的基礎,掌握了這些有利于我們之后的學習。下周我們將繼續F#的學習,一起探究F#的基礎

13、語法。3. 基本語法,模式匹配及ListF#是隨著VS2010 Beta版一起正式推出的另一個基于.NET平臺的語言,以函數式語言著稱。這個F#入門文章介紹了F#的基本語法,模式匹配及List基本類型。F#隨著VSTS 2010 Beta1 發布也有一段時間了,園子里應該也有不少人對它感興趣吧。下面的例子是我在學F# 基本語法時寫的一個簡單Sieve of Eratosthenes 實現,通過剖析這一小段代碼,我希望大家能對F#有個簡單認識,并能自己寫一些簡單的小程序。3.1 F#入門代碼1 let GetAllPrimesBefore n = 2 let container = Array.

14、create (n+1) 0 3 let rec loop acc = function 4 | -> List.rev acc 5 |hd:tl -> 6 if container.hd =1 then 7 loop acc tl 8 else 9 for j in hd . hd . n do 10 container.j <- 1 11 loop (hd:acc) tl 12 loop 2 . n 13 14 let primesBefore120 = GetAllPrimesBefore 120 進入正題let GetAllPrimesBefore n =第一行,申明

15、函數GetAllPrimesBefore, 并且該函數有一個參數n, 在這里我沒有指定n的類型,因為編繹器可以通過函數體對n的類型進去推斷,比如在本例中,n就是int類型,當然我們也可以顯示的指定n的類型,比如 let GetAllPrimesBefore (n:int),這樣我們就指定了n為int型 (注意:(n:int)中的括號不能省略,let GetAllPrimesBefore n : int 的意思是該函數返回的值的int型)。說完了參數,再說下返回值,同樣,編繹器會根據函數體上下文對返回值類型進去推斷,所以我們不需要申明返回類型。let container = Array.crea

16、te (n+1) 0第二行,首先請注意該行與第一行相對有一個縮進(TAB),F#和Python一樣,也是通過TAB縮進來組織代碼結構的。這一行我們定義了一個變量container,它的類型是Array,大小為 n+1, 并且值全部初使化為0接下來就是這個函數的主要部分了,首先我們定義了一個遞歸函數(我們發現定義遞歸函數需要加rec關鍵字)。它接受兩個參數,acc和一個List,有朋友可能要問了,這里明明我只看到一個參數acc,你說的那個List在哪呢?可能有細心的朋友也發現了這里的函數定義不光前面有rec,在等號后面還加了個function,那么function是做什么用的呢?3.2 F#入門

17、:模式匹配這里我需要首先講一下Pattern Matching(模式匹配), Pattern Matching有些類似于C#中的switch語句(當然它要比C#中的switch強大許多,但這不是本文的目地,所以略去不表),可以根據expr的值去執行某一具體分支,它的基本語法也很簡單,我們還是結合一個具體實例來看一下(例子比較簡單,只是為了說明問題)。 這個例子大家很容易看懂吧,我就不詳細解釋了,只是說明一點,'_'用來匹配所有別的情況。1 let ShowGreeting laguageInUse = 2 match laguageInUse with 3 | "C#

18、" -> printfn "Hello, C# developer!" 4 | "F#" -> printfn "Hello, F# developer!" 5 |_ -> printfn "Hello, other developers!" 因為Pattern Matching在F#中的使用范圍實在太廣了,所以就引入了一種簡化版,這就是上面大家看到的等號后面的function的作用,我們可以把上面的例子簡化成1 let ShowGreeting = function 2 | "

19、;C#" -> printfn "Hello, C# developer!" 3 | "F#" -> printfn "Hello, F# developer!" 4 |_ -> printfn "Hello, other developers!" 怎么樣?既少了給參數起名的煩惱,也少敲不少字吧,嘿嘿。3.3 F#入門:List基本類型接下來我再簡單介紹下F#中非常重要的一個基本類型List, 其基本表示形式為 item1;item2; . ;itemnF#中List是immutable

20、類型,我們只能訪問里面的值,不能改動里面的值,任何改動List的需求只能通過構建新的List來實現。稍一思考,大家就會很快發現要實現一個高效的immutable list, 那最簡單的就是對其頭結點進去操作了(插入和刪除都可以達到O(1),當然插入和刪除會構建一個新的List,原List不會改變),F#中的List也是基于這種形式,所有的List都可以看成是Head+Tail(除了Head外的所有結點),F#提供了相應的庫函數List.hd, List.tl,并且提供了: (cons operator)來幫助我們方便的構建一個List,比如1:2:就表示List 1;2 (注意1和2之間我用的

21、是;不是, 如果寫成1,2,那個表示該List只有一個元素 (1,2),至于(1,2)是什么類型,為了使文章盡量緊湊,我們今天就不講了)有了上面這些知識,再看本文一開始的函數就簡單多了1 let rec loop acc = function 2 | -> List.rev acc 3 |hd:tl -> 4 if container.hd =1 then 5 loop acc tl 6 else 7 for j in hd . hd . n do 8 container.j <- 1 9 loop (hd:acc) tl 首先,該函數的第二個參數是List,當List為空時

22、,就把acc反序返回,當List不為空時,把List分成兩部分(hd:tl),檢查當前值n (n的值等于td) 是否己被標記。如果己經被標記(container.hd =1),略過當前值,檢查接下來的值 loop acc tl;如果沒有被標記(當前值是素數),用當前值和acc構建一個新List (hd:acc),并對當前值的所有倍數進去標記(for loop),然后檢查下一個值 loop (hd:acc) tl這里有兩點需要特別說明一下:1. container是一個Array類型的參數,Array在F#中是mutable類型的容器,我們可以修改里面的元素,訪問元素用Array.i, 修改元素

23、用Array.<-i = newValue(不要忘記中間的.)2. for loop的基本形式為 for <index> in <range> do, 我們可以使用start . end或start . step . end來構建一個range,當然,這里的range其實也是一個List看完了內部函數,我們再接著往下看loop 2 . n這里就很簡單了,調用我們剛剛定義的內部函數,(acc為空List , 第二個參數為List 2 . n),其返回值(List acc)就是函數GetAllPrimesBefore的返回值,F#中函數有返回值時不需要敲return。

24、函數調用也很簡單,(不需要在參數與函數名之間加括號)后記1. F#中函數體內可以定義新的值,變量和函數。(只在當前函數體內可見)。當然,這樣做的好處顯而易見,我就不啰嗦了。2. Recursive function是functional programming中很常用的一種算法實現方式。functional programming language往往會針對尾遞歸進行特別的優化,F#也不例外,所以我們需要盡可能的把遞歸寫成尾遞歸的形式,這個有時就需要像本文一樣借助accumulator來實現。4. F#數據類型:Discriminator Union本文介紹了一個十分重要的F#數據類型:imm

25、utable的Discriminated Unions。它表示一組有限的可選情形,并且每種情形都有自己的嚴格定義。4.1 F#數據類型之Discriminator Union簡介我們通過一個簡單的例子了解了怎樣在F#中聲明變量,定義函數,并且用到了F#中兩個重要的數據類型List和Array,今天我主要介紹F#中一個非常重要的immutable數據類型Discriminated Unions。還是首先看一個例子,這是我寫的一個簡單的生成二分查找樹的例子。1 type Tree<'a> = 2 | Node of 'a * Tree<'a> * T

26、ree<'a> 3 | Nil 4 5 let generateBinarySearchTree l = 6 let rec insert a = function 7 | Node(root,left,right) when a < root -> 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

27、 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 我們首先來看前三行,沒錯,這就是今天要重點介紹的F#數據類型:Discriminator Union1 type Tree<'a> = 2 | Node of 'a * Tree<'a> * Tree<'a> 3 | Nil 首

28、先注意到我們這次使用的是type,而不是前面常用的let關鍵字。 F#中使用type關鍵字來定義用戶自定義類型,在這里我們定義了一個類型Tree, 那么Tree后面的<'a>又是啥意思呢?可能有的朋友己經猜到了,它表示a是一個泛型占位符,在實際使用中,a可能是int型,也可能是string等等(注意別忘了a前面的單引號)。后面二行就是具體的Tree定義了,它表示我們定義的Tree有兩種可能,有可能是Node,也有可能是Nil。 我們先來看第一種情形Node of 'a * Tree<'a> * Tree<'a>它表示Node的

29、類型是 'a * Tree<'a> * Tree<'a>, 那么這個又表示什么呢?其實它是F#中另外一種重要的immutable類型Tuple, Tuple很容易理解,它表示把一個數據集合在邏輯上看作是一個整體。看個例子大家就明白了(注意分隔符是逗號)let s2 = (1,"hello") (在這里我們定義了一個類型為int * string的Tuple. 要使用它里面的值也很簡單,我們可以聲明新的變量并用s的值來初始化它們。 let i,s = s2就表示我們聲明了int型變量i,它的值為1, string型變量s,它的值

30、是2)回到我們的例子中來, 'a * Tree<'a> * Tree<'a> 就很容易理解了,因為在定義Discriminated union時可以遞歸引用自己。Tree的第二種情形Nil很簡單,它表示一個什么都沒有的空結點.通過我上面詳細的解釋,我想大家也明白了什么是Discriminated union, 它表示一組有限的可選情形,并且每種情形都有自己的嚴格定義。回到我們上面的例子,Tree有兩種情形,要么是 'a * Tree<'a> * Tree<'a>的Node,要么是一個空的Nil。大家

31、也看到了它和Pattern matching結合使用非常頻繁,這下明白為什么叫Discriminated union了吧如果你認真讀到上一篇文章的話,接下來構建二分查找樹的代碼比較簡單,我就不解釋了。我們接下來看如何判斷某一個值是否在一個構建好的二分查找樹中。let rec tryFind x = function | Node(root,_,_) when x = root -> Some(x) | Node(root,left,_) when x<root -> tryFind x left | Node(root,_,right) when x > root -&

32、gt; tryFind x right | _ -> None 首先要注意我使用了五個'_',前面四個看起來好象和最后的一個有些不一樣。記得我在上一篇中說過'_'用在Pattern Matching中用來匹配所有別的情況,而且我說過F#里的Pattern Matching要比C#中的Switch強大,在這里我們就看到了它的強大之處,它可以在找到匹配后,為匹配的各部分綁定一個變量名來方便我們后面的調用,在綁定時如果我們僅僅對某些部分感興趣,那么我們就可以使用'_'來代替我們不感興趣的部分(注意'_'只能綁定一個對應部分,要對應

33、兩個我們就要敲兩個'_','_').其次我們注意到tryFind的返回值好象有兩種情況呀,Some(x)和None,一個函數怎么能返回兩種不同類型的值呢? 呵呵,忘記我們今天主要在講Discriminated union了?這是個F#里事先定義好的一個discriminated union,它有自己的名字叫Option,它的定義非常簡單,有了前面的基礎,這個就不需要我解釋了吧。 1 type option<'a> = 2 |Some of 'a 3 |None 總結:今天我主要說了F#中非常重要的一種immutable類型Discri

34、minated union,并順帶說了下另兩個簡單的類型Tuple和Option。簡單的functional programming知識就剩下最重要function沒有說了,下一篇我主要來說說F#里的函數,希望在下一篇后,大家不再覺得F#難懂難用了。5. F#語法精要F#基本語法的學習是基于對F#類型系統和類型推斷機制的理解的。在本節教程中,我們將學習到F#的變量聲明和程序流程結構。在上一節F#教程中,我們對F#的類型系統和類型推斷機制有了一個初步的認識。F#的類型推斷原理是學習F#的重要基礎。本節課程,我們將在F#類型基礎上進一步學習F#的一些基本語法。“let”表達式是F#語法的核心,可以用作定義函數、序列等多種用途。另外,F#使用空格來標記程序塊的開始與結束。定義值1 let x = 2 定義函數值 2 let f a = a + x 定義循環函數3 open System.IO 4 let rec printSubDirFiles dir = 5

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論