????不得不說(shuō)SCIP(Structure and Interpretation of Computer Programs)是我至今以來(lái)看過(guò)的讓我最受益的一本教材了膜蠢。它開(kāi)啟了我程序設(shè)計(jì)世界里的另一扇門(mén)瘟檩,我有時(shí)會(huì)驚嘆:原來(lái)程序可以這樣寫(xiě)办斑!這里我將自己的讀書(shū)心得分享給大家,既然是讀書(shū)心得,不到之處,還請(qǐng)大家指正。
????書(shū)中所有的例子都采用Scheme語(yǔ)言編寫(xiě)赃磨,因此我這里涉及到代碼的地方也會(huì)使用Scheme。初次看Scheme語(yǔ)句的時(shí)候你可能會(huì)十分別扭洼裤,但是當(dāng)你逐步深入學(xué)習(xí)時(shí)邻辉,你會(huì)發(fā)現(xiàn)Scheme語(yǔ)言的強(qiáng)大之處。我這里不會(huì)去評(píng)論各種編程語(yǔ)言的好壞逸邦,存在即是理由恩沛,不管是歷史造就了一門(mén)語(yǔ)言,或是某個(gè)產(chǎn)品推動(dòng)了一門(mén)語(yǔ)言的發(fā)展缕减。每種語(yǔ)言都有自己的優(yōu)點(diǎn)與缺點(diǎn)雷客,在特定的場(chǎng)景下使用適當(dāng)?shù)恼Z(yǔ)言,才能充分發(fā)揮程序的作用桥狡。就像生活中有各種各樣的刀一樣搅裙,有削鉛筆的刀,有切菜的刀裹芝,有砍骨頭的刀...每種刀只有在特定的場(chǎng)景下才能充分展示其作用部逮,你不可能拿砍骨頭的刀去削鉛筆,反之嫂易,你也不會(huì)拿削鉛筆的刀去砍骨頭兄朋。語(yǔ)言就是一門(mén)工具,我們說(shuō)學(xué)習(xí)一門(mén)語(yǔ)言怜械,無(wú)外乎學(xué)習(xí)這門(mén)語(yǔ)言的語(yǔ)法規(guī)則颅和,這是一件十分簡(jiǎn)單的事,我們可以在幾個(gè)小時(shí)內(nèi)就熟悉缕允,但是我們其實(shí)更應(yīng)該學(xué)習(xí)的是分析和解決問(wèn)題的能力峡扩。寫(xiě)代碼就是為了解決現(xiàn)實(shí)生活中的一些問(wèn)題,遇到一個(gè)問(wèn)題我們以什么方式能夠高效優(yōu)雅地解決障本,這才是一個(gè)程序員能力的體現(xiàn)教届。引用書(shū)中的一個(gè)比喻响鹃,我覺(jué)得十分恰當(dāng):學(xué)習(xí)一門(mén)語(yǔ)言就像學(xué)習(xí)下棋,語(yǔ)言的語(yǔ)法就像下棋的規(guī)則案训,就像象棋里的馬走日买置,象飛田...每個(gè)人都能夠快速的上手,但是要稱(chēng)為下棋的高手强霎,光懂這些規(guī)則是遠(yuǎn)遠(yuǎn)不夠的堕义,還需要學(xué)習(xí)比如開(kāi)局,戰(zhàn)術(shù)脆栋,策略...因此作為程序員,我們更應(yīng)該追求后者洒擦,提高自己分析和解決問(wèn)題的能力椿争,而不是炫耀學(xué)了多少種語(yǔ)言。好了熟嫩,扯了很多不相關(guān)的東西秦踪,下面正式步入正題。
在這一篇中掸茅,我們主要熟悉Scheme的語(yǔ)法和一些基本概念椅邓。
1. 什么是程序設(shè)計(jì)語(yǔ)言?
書(shū)的開(kāi)篇就提出了一個(gè)問(wèn)題:什么是程序設(shè)計(jì)語(yǔ)言昧狮?程序設(shè)計(jì)語(yǔ)言需要具備什么特性景馁?
程序設(shè)計(jì)語(yǔ)言就是一種指示機(jī)器去執(zhí)行任務(wù)的方法,另外一方面逗鸣,程序設(shè)計(jì)語(yǔ)言需要提供給其使用者一種表達(dá)其思想的方法合住。
上面是我翻譯的,原文是這樣的:
A powerful programming language is more than just a means for instructing a computer to perform tasks. The language also serves as a framework within which we organize our ideas about processes.
從這里我們可以看出撒璧,程序設(shè)計(jì)語(yǔ)言其實(shí)充當(dāng)著翻譯官的作用透葛,它連接著程序員和機(jī)器,將程序員的思想翻譯給機(jī)器執(zhí)行卿樱。仔細(xì)想想僚害,好像是這么一回事。強(qiáng)大的程序設(shè)計(jì)語(yǔ)言都具備以下三種機(jī)制:
- 元表達(dá)式:程序設(shè)計(jì)語(yǔ)言中的最簡(jiǎn)單的實(shí)體
- 組合方式:通過(guò)組合方式將簡(jiǎn)單的實(shí)體組合成更加復(fù)雜的元素
- 抽象能力:組合的元素能夠被命名并作為一個(gè)整體來(lái)使用
所謂的元表達(dá)式就是程序中最簡(jiǎn)單的元素繁调,主要包括兩種:數(shù)字和元操作符+萨蚕,-,*涉馁,/门岔。元操作符和數(shù)字可以進(jìn)行組合,例如在Scheme語(yǔ)言中你可以這樣寫(xiě):
(+ 1 2 3 4) -> 10
(* 2 3 5) -> 30
這樣表示方法叫前綴表達(dá)式烤送,語(yǔ)法規(guī)則是
(操作符 操作數(shù) 操作數(shù) ...)
這樣表示法相比于我們熟悉的中綴表達(dá)式的一個(gè)最大的好處是一個(gè)表達(dá)式可以有任意多個(gè)操作數(shù)而不會(huì)產(chǎn)生歧義寒随,就像上面的例子。前綴表達(dá)式的另一個(gè)優(yōu)勢(shì)在于表達(dá)式的嵌套,例如:
(+ (* 2 3) (- 10 3)) -> 13
(注:要運(yùn)行這些例子妻往,需要下載一個(gè)Scheme解釋器互艾,例如MIT的scheme解釋器)
????關(guān)于組合方式這一塊,我們可以回顧下C語(yǔ)言和Java語(yǔ)言讯泣。在C語(yǔ)言中我們可以使用結(jié)構(gòu)體struct和聯(lián)合體union來(lái)簡(jiǎn)單的元素組合成復(fù)雜的元素纫普;在Java語(yǔ)言中,我們使用類(lèi)class來(lái)組合各種簡(jiǎn)單的元素好渠,相比于結(jié)構(gòu)體struct昨稼,class中還可以定義方法(當(dāng)然,結(jié)構(gòu)體中也可以定義函數(shù)指針變量來(lái)實(shí)現(xiàn)等價(jià)的效果)拳锚,因此Java語(yǔ)言的組合方式更多一點(diǎn)假栓。在Scheme語(yǔ)言里提供的組合方式叫做pair,我們可以使用box-and-pointer的表示方法來(lái)表示一個(gè)pair霍掺。在box-and-pointer中匾荆,每個(gè)實(shí)體都表示為一個(gè)box和指向該box的一個(gè)pointer,元表達(dá)式的box中就存放著與之對(duì)應(yīng)的內(nèi)容杆烁,而pair可以看做是兩個(gè)連在一起的box和指向這兩個(gè)box的pointer牙丽。一圖勝千言:
一個(gè)pair可以使用Scheme提供的cons函數(shù)來(lái)創(chuàng)建,如:
(cons 1 2) 就構(gòu)建了上圖中的pair
就像結(jié)構(gòu)體struct中可以嵌套結(jié)構(gòu)體兔魂,class中可以定義class一樣烤芦,這里的cons中也可以嵌套cons,例如:
(cons 1 (cons 2 (cons 3 (cons 4 nil))))
用圖形表示一下:
是不是像某個(gè)數(shù)據(jù)結(jié)構(gòu):鏈表析校?(表達(dá)式中nil可以和我們熟悉的null做類(lèi)比)這種嵌套(閉包特性:若某種操作的結(jié)果仍然可以用該操作處理拍棕,那么我們說(shuō)這個(gè)操作有閉包特性,例如這里的cons)十分強(qiáng)大勺良,能夠表達(dá)各種各樣的數(shù)據(jù)結(jié)構(gòu)绰播,這個(gè)后續(xù)還會(huì)詳細(xì)介紹。
抽象能力這塊其實(shí)是程序員需要重點(diǎn)關(guān)注的地方尚困,看待一個(gè)問(wèn)題的時(shí)候蠢箩,我們應(yīng)該盡可能地從更高層次上去抽象出問(wèn)題的本質(zhì)并找到合適的解決方案。在程序設(shè)計(jì)語(yǔ)言中的抽象能力主要體現(xiàn)在一個(gè)實(shí)體可以被命名事甜,通過(guò)名字來(lái)引用某個(gè)實(shí)體谬泌。
????從元表達(dá)式、組合方式逻谦、抽象能力這三點(diǎn)我們可以總結(jié)一下:程序設(shè)計(jì)其實(shí)和搭積木很類(lèi)似掌实,我們有一個(gè)個(gè)簡(jiǎn)單的積木(元表達(dá)式),可以通過(guò)膠水或其他方式將一些簡(jiǎn)單的積木綁在一起(組合方式)得到組合的積木邦马,最后我們用組合的積木(抽象能力)構(gòu)建成最終的形狀贱鼻。反過(guò)來(lái)宴卖,我們要解決某個(gè)具體的問(wèn)題,我們需要用抽象的能力將問(wèn)題分解成一個(gè)個(gè)子問(wèn)題邻悬,這些子問(wèn)題能夠更加簡(jiǎn)單的方式解決症昏,當(dāng)子問(wèn)題解決后,我們將子問(wèn)題的解組成原始問(wèn)題的解父丰。
2.Scheme基本語(yǔ)法
學(xué)習(xí)一門(mén)語(yǔ)言肝谭,當(dāng)然最開(kāi)始的就是學(xué)習(xí)其語(yǔ)法。這里主要簡(jiǎn)單的講下基本語(yǔ)法蛾扇,有興趣的同學(xué)可以看書(shū)深入了解攘烛。
- 變量與函數(shù)的定義
定義變量使用(define 變量名 變量值) 如 (define PI 3.14)
定義函數(shù)使用(define (函數(shù)名 形式參數(shù)列表) 函數(shù)體) 如 (define (sum a b) (+ a b)) - 條件語(yǔ)句與預(yù)言
所謂的預(yù)言就是一個(gè)布爾表達(dá)式,有真和假兩種情況镀首,也就是我們常用的邏輯表達(dá)式:
邏輯與 (and <e1> <e2> ... <en>) 當(dāng)<e1>, <e2> ... <en>都為真時(shí)為真医寿,有短路規(guī)則.
邏輯或 (or <e1> <e2> ... <en>) 當(dāng)<e1>, <e2> ... <en>任意一個(gè)為真時(shí)為真,有短路規(guī)則
邏輯非 (not <e>) <e>為真時(shí)表達(dá)式為假蘑斧,<e>為假時(shí)表達(dá)式為真
條件語(yǔ)句的語(yǔ)法是:
(cond (<p1> <e1>)
(<p2> <e2>)
...
(<pn> <en>))
類(lèi)似于我們熟悉預(yù)言里的switch語(yǔ)句,其執(zhí)行順序?yàn)槿魀1預(yù)言為真须眷,那么就執(zhí)行e1竖瘾,同時(shí)e1表達(dá)式的結(jié)果將作為條件語(yǔ)句的結(jié)果返回。若p1為假花颗,那么就判斷p2預(yù)言捕传,以此類(lèi)推。條件語(yǔ)句還有一種扩劝,就是我們常用的if語(yǔ)句庸论,其在Scheme里的語(yǔ)法為:
(if 預(yù)言 預(yù)言為真執(zhí)行的語(yǔ)句 預(yù)言為假執(zhí)行的語(yǔ)句) 例如 (if (> x 0) (+ x 1) (- x 1))
if語(yǔ)句是一種條件受限的cond語(yǔ)句,它只有兩種情況棒呛。也就是說(shuō)所有的if語(yǔ)句可以轉(zhuǎn)換為cond語(yǔ)句聂示,反之則不能。
????Scheme的基本語(yǔ)法就這么多簇秒,你或許會(huì)問(wèn)鱼喉,怎么沒(méi)有循環(huán)表達(dá)式?這也是我當(dāng)時(shí)的困惑趋观,在Scheme中使用遞歸來(lái)代替了循環(huán)表達(dá)式,詳細(xì)在后文中講述。現(xiàn)在應(yīng)該了解了Scheme的基本語(yǔ)法了疯坤,可以嘗試用Scheme實(shí)現(xiàn)下書(shū)中的例子求某個(gè)數(shù)x的平方根僧凤。有疑問(wèn)?請(qǐng)留言剩辟。