操作符(operator)
f#中,可把操作符看作一種函式呼叫的更為優雅的方式。操作符有兩種:字首(prefix)和中綴(infix),前者接受乙個運算元(operand),出現在運算元之前;後者接受兩個或多個運算元,出現在頭兩個運算元之間。
f#提供了豐富的操作符集,可用於數字、布林值、字串和集合型別。這些操作符數量甚眾,限於篇幅,在此不再一一詳解。本文將著重介紹如何使用和定義操作符。
類似於c#,f#的操作符也可以過載,也就是說,我們可以將不同的型別用於同一操作符,如「+」;但是與c#不同的是,各個運算元必須為相同的型別。f#的操作符過載規則與c#類似,因此任何bcl或者使用c#編寫的.net類庫中的支援操作符過載的類在f#中一樣支援過載。
letwords="tolive"+"is"+"tofunction."
opensystem
letoneyearlater=datetime.now+newtimespan(365,0,0,0,0)
我們可以定義自己的操作符,也可以重定義已有的任何操作符(不建議這樣做)。看看下面這種不好的做法:
let(+)ab=a–b
print_int(1+2)
看到這裡,你想到了什麼?是不是:這分明是在定義乙個函式嘛!所以我們前面說「可把操作符看作一種函式呼叫的更為優雅的方式」。我們重定義了「+」操作符,所以「1 + 2」的結果為-1,這當然沒什麼好處,在vs中使用fsi時,怎樣把「+」改回它原本的含義呢?我一般在任務管理器中把fsi程序關掉,再按回車,「+」就回來了。
自定義的操作符不能包含字母和數字,可以使用的字元如下:
!$%&+-./<=>?@^|~
操作符的第乙個字元可以是上面第一行的任意字元,其後的字元則可以是上面的任意字元了。定義語法與函式類似,除了要將操作括起來。看下面的例子:
let(+^*)ab=(a+b)*(a*b)
結果為30。
列表(lists)
列表是內置於f#的簡單集合型別。可以是乙個空表(empty list),使用方括號表示()。我們可以將乙個值與列表連線,此時要使用「::」操作符,注意要將值作為第乙個運算元:
letemptylist=
letoneitem="one"::
lettwoitem="two"::oneitem
在vs中可以看到oneitem的型別為string list。如果列表包含多個項,用上面的方法顯得麻煩了,我們可以使用下面的語法:
letshorthand=["hello";"world!"]
另外我們還可使用「@」操作符來連線兩個列表:
letconcatenatelists=["one,";"two,"]@["three,";"four"]
f#要求列表中的元素型別必須是相同的,如果你確實需要列表包含不同型別的元素,那只好建立乙個obj(即system.object)型別的列表了:
letobjlist=[box1;box2.0;box"three"]
其中第三個box是可選的,這個讓我想起了c#中的裝箱。
f#中的列表是不可修改的,一旦建立就不能修改了。作用於列表的函式和操作符也不能修改列表,而是建立了列表的乙個副本。這個特性很像c#中的string型別。看下面的例子:
#light
letprintlistlist=
list.iterprint_stringlist
print_newline()
letthreeitems=["one";"two";"three"]
letreversedlist=list.revthreeitems
printlistthreeitems
printlistreversedlist
上面的iter方法接受兩個引數,第乙個是函式,第二個是列表,其作用是將函式依次應用於列表的每個元素,有點像c#中的foreach迴圈。而rev方法則返回列表的逆序列表。
列印結果為:
one tow three
three two one
列表推導(list comprehensions)
最簡單的情況是指定列表的範圍,如:
letnumericlist=[0..9]
letcharlist=['a'..'z']
這兩個列表的型別分別是int list和char list,範圍分別是從0到9和從』a』到』z』。
更複雜的情況是指定乙個步長:
letmultipleofthree=[0..3..30]
letrevnumericlist=[9..-1..0]
第乙個列表的值是0到30間所有3的倍數,第二個列表的元素則包含了從9遞減至0。
我們還可以通過對乙個列表進行迴圈操作得到另乙個列表。例如:
letsquares=[forxin1..10->x*x]
通過for進行迴圈,squares列表的元素是1到10間的整數的平方。
此外還可以為迴圈新增when子句對元素進行過濾,只有when子句的值為true時才對其進行運算:
letevens=[forxin1..10whenx%2=0->x]
evens的元素為[2; 4; 6; 8; 10]。
控制流程(control flow)
f#擁有強的控制流程概念,這與很多純函式式程式語言不同,在這些語言中表示式可以以任何順序進行求值。看下面的例子:
letabsolutevaluex=
ifx<0then
-xelifx=0then
0else
x
if, elif, then, else組成的結構我們應當很熟悉,在f#中該結構是乙個表示式,也就是說它需要返回乙個值。而且每個分支返回的值應當具有相同的型別,否則就會有編譯錯誤。如果確實要返回多個型別的值,在值前加box關鍵字,就像前面建立列表時那樣,這樣表示式的返回型別為obj。
型別與型別推導(types and type inference)
f#是一種強型別的語言,傳給函式的值必須是指定的型別。如果函式接受string型別的引數,就不能傳給它int型別的值。一種語言處理其中值的型別的方式稱為語言的型別系統。f#的型別系統與一般語言不同,包括函式在內,所有的值都具有自己的型別。
通常情況下,我們不需要顯式地宣告型別,編譯器會嘗試從值的文字值或呼叫的函式返回型別來判斷其型別,這個過程稱為型別推導。可在編譯時使用-i開關來顯示所有的推導型別,在vs中我們則可以使用工具提示來檢視識別符號的型別。先看下面值的型別推導情況:
letstrvalue="stringvalue"
letintvalue=12
在fsi中可看到它們的資訊是:
valstrvalue:string
valintvalue:int
可以理解,編譯器跟據賦給識別符號的文字值來推導其型別。再看看下面函式的情況:
letmakemessagex=(string_of_boolx)+"isabooleanvalue"
lethalfx=x/2
在fsi中可看到它們的資訊是:
valmakemessage:bool->string
valhalf:int->int
有意思的是,函式名前面也有個val,這表明函式也是值,後面的bool -> string是什麼意思呢?它表明函式接受bool型別引數,返回string型別的值,注意x作為string_of_bool的引數,所以x必須為bool型別,返回值是兩個字串相加的值,故返回值也是string型別。對於half函式,單從定義不能確定x型別,此時編譯器採用預設的型別int。再看看稍微複雜點的情況:
letdiv1xy=x/y
letdiv2(x,y)=x/y
這兩個函式的資訊是:
valdiv1:int->int->int
valdiv2:int*int->int
div1函式可接受部分引數(可柯里化),而div2則必須同時傳入兩個int型別的值。考慮下面的函式:
letdonothingx=x
其資訊為:
valdonothing:'a->'a
a』 -> a』表示函式接受任意型別,並返回與其相同型別的值。以』打頭的型別表示可變型別(variable type),編譯器雖然不能確定型別的引數,卻能確定返回值型別必須與引數型別相同,型別系統的這種特性稱為型別引數化,通過它編譯器也能發現更多的型別錯誤。可變型別或型別引數化的概念,類似於.net 2.0的泛型,如果f#基於支援泛型的cli,那麼它會充分利用泛型的優勢。另外,f#的建立者don syme,正是clr中泛型的設計者和實現者。
f#的型別推導固然強大,但它顯然不能揣測出開發人員所有的心思來,如果有特殊需求該怎麼辦呢?看下面的例子:
letdonothingtofloat(x:float32)=x
float32即system.single,這裡我們手動指定了x的型別,這個有時稱為型別標註(type annotation)。如果要在f#中使用其它.net語言編寫的類庫,或者與非託管的類庫進行互操作,它會很有用。
小結參考:
《foundations of f#》 by robert pickering
《f# specs》
函式式程式語言F
文 高昂 作為微軟支援的第乙個函式式語言,f 在專案中被越來越多的開發者選用,8月的tiobe排行榜,f 挺進前二十。源於微軟研究院的f 語言因其優良的設計和強大的並行程式設計能力,正得到越來越多.net開發者的選用。在8月的tiobe語言流行度排行榜中,f 語言首次進入了前二十位。f 是微軟.ne...
F 程式設計 函式式程式設計之Records
當你想把資料組成乙個結構化的格式,而不需要太複雜的語法時,你可以使用f 中的record型別。record型別與c語言的struct型別基本一樣,儲存一組型別的值,通過欄位的值來獲取。定義乙個record型別很簡單,只需要在大括號內定義系列的名稱 型別就可以。要例項化乙個record,只需要提供對應...
函式式程式設計之 初窺F
大量講解函式式程式語言的書籍最終都會用fuctor,monad,monoids,範疇論等各種詞彙嚇退命令式語言玩家,所以我試圖避開這些問題,揭開這些複雜詞彙帶來的具有實戰意義的成果。另外我會盡量使用c 語言來描述函式式程式設計思想,因為c 某些語法和特性來自於函式式語言的啟發,但c 終究並不是正統的...