Standard ML快餐教程(1) - 初識(shí)
好久沒寫快餐教程了旱物,下面開始一個(gè)新的系列,關(guān)于函數(shù)式編程語言的系列卫袒。打算寫三種語言:Standard ML宵呛,ocaml和Haskell。
這幾門語言都不是新貴了夕凝,其中Standard ML的知名度可能最低宝穗。因?yàn)镸L系列的影響力,其實(shí)我們已經(jīng)從新的語言如rust等中學(xué)到過這些老語言的很多知識(shí)了÷氡現(xiàn)在我們可能只要將它們還原回去就好逮矛。
運(yùn)行環(huán)境
Standard ML,顧名思義转砖,是ML語言的一種標(biāo)準(zhǔn)規(guī)范须鼎,主要的版本有SML 90和SML 97。
標(biāo)準(zhǔn)的實(shí)現(xiàn)就有很多種方式府蔗。比如以交互式編譯為主要特點(diǎn)的SML/NJ莉兰,還有以優(yōu)化編譯為主要目標(biāo)的mlton。另外一個(gè)重要的Standard ML編譯器是劍橋大學(xué)的David Matthews開發(fā)的poly/ML礁竞,因?yàn)橹耐评碜C明工具Isabelle是使用polyml開發(fā)的。
SML/NJ是由貝爾實(shí)驗(yàn)室的David MacQueen和普林斯頓大學(xué)的Andrew Appel于1986年發(fā)起的杉辙。為了滿足1997年SML的修訂模捂,1998年1月推出了110版本。時(shí)至2020年,主版本號(hào)還是110狂男,比如我現(xiàn)在用的版本是v110.97综看。
SML/NJ的項(xiàng)目主頁在:http://www.smlnj.org/。其源代碼是使用svn管理的岖食。mlton和polyml的源代碼都在github上管理红碑。mlton的地址為:https://github.com/MLton/mlton,ployml的地址為:https://github.com/polyml/polyml/泡垃。
安裝的話析珊,因?yàn)橐陨先N語言工具都屬于比較通用成熟的工具,在mac上的homebrew和Ubuntu之類的主流Linux發(fā)布版本中都有支持蔑穴。
比如在macOS上忠寻,可以使用homebrew進(jìn)行安裝這三種環(huán)境:
brew install mlton
brew install polyml
brew cask install smlnj
在Ubuntu下可以使用下面的命令安裝:
apt install smlnj
apt install mlton
apt install polyml
安裝好之后,運(yùn)行sml命令存和,就可以進(jìn)入sml/nj的交互式環(huán)境奕剃,就可以開始學(xué)習(xí)編寫Standard ML代碼了,"-"是smlnj的提示符:
Standard ML of New Jersey v110.79 [built: Sat Oct 26 12:27:04 2019]
-
我們可以先寫個(gè)HelloWorld程序練練手,比如叫hello.sml:
val a = 1 :int ;
print "Hello,World\n";
smlnj可以通過sml hello.sml來調(diào)用捐腿。
也可以通過mlton hello.sml來編譯纵朋,然后會(huì)生成hello,運(yùn)行hello就可以看到運(yùn)行結(jié)果茄袖。
使用polyc進(jìn)行編譯的時(shí)候操软,會(huì)報(bào)下面的錯(cuò)誤:
poly: : error: Value or constructor (main) has not been declared
Found near PolyML.export (List.nth (CommandLine.arguments (), 3), main)
Static Errors
這是因?yàn)閜olyml需要一個(gè)main函數(shù)。
我們寫個(gè)main函數(shù)吧:
fun main() = print "Hello,World\n";
然后調(diào)用polyc去編譯:
polyc hello2.sml -o hello2
最后運(yùn)行hello2就可以看到運(yùn)行結(jié)果绞佩。
polyml也可以通過poly命令交互式運(yùn)行寺鸥。
Poly/ML 5.8.1 Release
提示符為">"。
數(shù)據(jù)類型
我們可以通過交互式界面來了解下基本數(shù)據(jù)類型的用法品山。
數(shù)字類型
比如我們輸入一個(gè)整數(shù)1:
> 1;
val it = 1: int
我們可以看到胆建,這樣一個(gè)整數(shù)被賦給一個(gè)叫it的默認(rèn)變量,其類型為int.
-1在Standard ML中表示為"~1"肘交,我們來看下:
> val a = 0 - 1;
val a = ~1: int
符點(diǎn)數(shù)是real類型:
> val b = 1e8;
val b = 100000000.0: real
字符串
Standard ML支持字符串類型:
> val d = "Hello";
val d = "Hello": string
布爾值
Standard ML使用true和false兩個(gè)關(guān)鍵字來表示bool類型:
> true;
val it = true: bool
> false;
val it = false: bool
> 1>0;
val it = true: bool
空值
Standard ML的空類型為unit笆载,用來表示一個(gè)空表():
> ();
val it = (): unit
> val g = ();
val g = (): unit
同樣,空的記錄也是unit:
> val x3 = {};
val x3 = (): unit
字符類型
通過“#”涯呻,也可以描述char類型:
> val e = #"c";
val e = #"c": char
ord函數(shù)用來將字符轉(zhuǎn)成整數(shù)凉驻,chr相反將ASCII值轉(zhuǎn)成字符。
> val x4 = ord(#"a");
val x4 = 97: int
> val x5 = chr(65);
val x5 = #"A": char
數(shù)據(jù)結(jié)構(gòu)
元組
元組是使用括號(hào)描述的列表复罐。類型將成為幾個(gè)類型之乘積涝登。
比如(3.0, true)的類型是real * bool.
> val x6 = (3.0, true);
val x6 = (3.0, true): real * bool
列表
不同于元組,列表的元素必須是相同的效诅,用方括號(hào)表示胀滚。
空列表不是unit趟济,也是個(gè)列表類型。
- val a1 = [];
val a1 = [] : 'a list
只要類型相同就可以咽笼,元組也沒問題:
- val a2 = [1,2];
val a2 = [1,2] : int list
- val a3 = ["h","e"];
val a3 = ["h","e"] : string list
- val a4 = [(3,4),(2,5)];
val a4 = [(3,4),(2,5)] : (int * int) list
記錄
類似于哈希表的一種結(jié)構(gòu)顷编,指定key與value:
- val a5 = {name="xulun",age=20};
val a5 = {age=20,name="xulun"} : {age:int, name:string}
如果以數(shù)字1,2,3等為key,則建立的記錄就是元組剑刑。我們看個(gè)例子:
- val a7 = {1=true, 2=3.0};
val a7 = (true,3.0) : bool * real
多賦值
Standard ML支持模式匹配多賦值媳纬,比如我們使用變量元組和值元組,舉個(gè)例子看下就清楚了:
> val (x1,x2) = (1.0, true);
val x1 = 1.0: real
val x2 = true: bool
語句
函數(shù)
可以通過fun來定義函數(shù)施掏,我們直接看個(gè)求平方的例子:
> fun s2 (x:int):int = x*x;
val s2 = fn: int -> int
> s2(4);
val it = 16: int
可以不指定類型钮惠,由系統(tǒng)進(jìn)行推斷:
- fun add1 x y = x + y;
val add1 = fn : int -> int -> int
如果想用浮點(diǎn)類型,可以指定一個(gè)其监,其它由系統(tǒng)推斷:
- fun add2 (x:real) y = x + y;
val add2 = fn : real -> real -> real
注釋
Standard ML使用(**)來寫注釋萌腿。
條件語句
Standard ML支持if then else語句,如果if為真則返回then后面的表達(dá)式抖苦,否則返回else分支的結(jié)果毁菱。
- fun neg1 (x:int) : bool = if x < 0 then true else false;
val neg1 = fn : int -> bool
還記得-1在sml表示為~1吧,我們測試下上面的if判斷:
- neg1(1);
val it = false : bool
- neg1(~1);
val it = true : bool
case語句
通過case語句锌历,可以處理多分支的條件贮庞,每個(gè)條件進(jìn)行自己的匹配。格式為case x of x1 => something1 | x2 => something2 | ...
我們來看個(gè)例子:
- fun grade1(x:int) = case x of 0 => false | 1 => true | x => false;
val grade1 = fn : int -> bool
- grade1(1);
val it = true : bool
- grade1(0);
val it = false : bool
- grade1(100);
val it = false : bool
標(biāo)準(zhǔn)庫
做為一種標(biāo)準(zhǔn)語言究西,Standard ML也支持可移植性的標(biāo)準(zhǔn)庫窗慎。我們可以調(diào)用這些庫像其它語言一樣愉快地編程了。sml的標(biāo)準(zhǔn)庫叫做BASIS卤材。
第一次調(diào)用的時(shí)候遮斥,basis會(huì)被加載進(jìn)來:
- Char.isUpper(#"a");
[autoloading]
[library $SMLNJ-BASIS/basis.cm is stable]
[library $SMLNJ-BASIS/(basis.cm):basis-common.cm is stable]
[autoloading done]
val it = false : bool
再來幾個(gè)例子,比如計(jì)算實(shí)數(shù)的絕對(duì)值:
- Real.abs(~1.5);
val it = 1.5 : real
整數(shù)轉(zhuǎn)實(shí)數(shù):
- val a10 = Real.fromInt(1);
val a10 = 1.0 : real
求平方根:
- val a11 = Math.sqrt(2.0);
[autoloading]
[autoloading done]
val a11 = 1.41421356237 : real
字符串連接:
- val a11 = String.concat(["Hello","World"]);
val a11 = "HelloWorld" : string
一起看起來都不錯(cuò)扇丛。類型术吗,分支,函數(shù)等都有了帆精。還有強(qiáng)大的標(biāo)準(zhǔn)庫较屿,恭喜你,已經(jīng)在Standard ML的世界里活下來了 :)