冒號(hào)和他的學(xué)生們
——程序員提高班紀(jì)事
16.動(dòng)態(tài)語(yǔ)言
Freedom is not free ——Kelly Strong
嘆號(hào)急不可耐地問(wèn):“現(xiàn)在可以談動(dòng)態(tài)語(yǔ)言了吧?”
冒號(hào)感言:“曾幾何時(shí),動(dòng)態(tài)語(yǔ)言還只是陪太子讀書的角色,那時(shí)候它們的名字是‘腳本語(yǔ)言’。近來(lái)卻迅速崛起,儼然有與靜態(tài)語(yǔ)言分庭抗禮之勢(shì)。”
問(wèn)號(hào)忍不住問(wèn)道:“動(dòng)態(tài)語(yǔ)言與腳本語(yǔ)言是一回事嗎?”
“相比動(dòng)態(tài)語(yǔ)言定義上的模糊,腳本語(yǔ)言的概念還是比較明確的。”冒號(hào)回避直接給出答案,“腳本(script)的提法,是為了區(qū)別于一般的程序(program)。Perl的發(fā)明者Larry Wall不愧為語(yǔ)言學(xué)家,對(duì)此有一個(gè)精彩的說(shuō)法:‘A
script is what you give the actors, a program is what you give the audience’。直譯為:腳本是給演員看的,節(jié)目是給觀眾看的。此言妙在一語(yǔ)雙關(guān)——program兼有‘節(jié)目’和‘程序’的意思。”
句號(hào)領(lǐng)會(huì):“這里的演員指的是程序員,觀眾指的是用戶。換言之,程序是為終端用戶服務(wù)的,而腳本是為程序員服務(wù)的。”
“正解!”冒號(hào)肯定道,“腳本最常見(jiàn)的形式是殼腳本(shell
script),在非Unix類的操作系統(tǒng)中也稱為批處理文件(batch file)。”
“批處理文件倒是很熟悉,殼腳本聽(tīng)起來(lái)就怪怪的。”逗號(hào)嘀咕著。
“那是因?yàn)槟阍?/span>Windows的世界里長(zhǎng)大,聽(tīng)不慣Unix的方言。”冒號(hào)一語(yǔ)道破緣由,“操作系統(tǒng)的內(nèi)核稱為核(kernel),出于安全考慮不便直接與用戶交互,因此裹上一層殼(shell),即人們常說(shuō)的命令行解釋器(command
line interpreter)。殼腳本是在殼上運(yùn)行的腳本,擴(kuò)展了命令行下可執(zhí)行的命令。它最初主要是內(nèi)建(built-in)命令的組合,用于系統(tǒng)程序的調(diào)度,是系統(tǒng)管理員的必備武器。其后,殼腳本也發(fā)展到用于應(yīng)用程序的調(diào)度、連接、調(diào)試等,成為粘合(glue)語(yǔ)言。”
逗號(hào)不禁有些疑問(wèn):“難道一般的程序語(yǔ)言如C之類的不能作此用嗎?”
引號(hào)回應(yīng)道:“這些語(yǔ)言通常需要‘編寫-編譯-鏈接-運(yùn)行’的過(guò)程,十分繁瑣。腳本語(yǔ)言編寫后即可運(yùn)行,快捷方便得多。”
冒號(hào)點(diǎn)點(diǎn)頭:“不錯(cuò),既然腳本主要用于整合其他程序,本身并不占用太多的資源,同時(shí)邏輯也不太復(fù)雜,因此腳本語(yǔ)言注重簡(jiǎn)潔、實(shí)用,語(yǔ)法要求不那么嚴(yán)格,性能上的要求也不高。除殼腳本外,還有一些專用于文本處理(Text
Processing)的語(yǔ)言或工具如AWK、sed和grep等,多用于讀寫配置文件和日志文件、過(guò)濾處理各種程序的輸入和輸出,對(duì)于整合各種程序也非常實(shí)用。隨著對(duì)腳本語(yǔ)言需求的增長(zhǎng),其局限性日益突出,Perl之類的高級(jí)腳本語(yǔ)言便應(yīng)運(yùn)而生了。Perl在殼腳本、AWK、sed的基礎(chǔ)上,融合了命令式的C與函數(shù)式的Lisp的特征,漸漸成為最流行的腳本語(yǔ)言之一。”
問(wèn)號(hào)注意到:“Javascript是瀏覽器端的腳本,來(lái)路似乎有些不同。”
冒號(hào)解釋道:“除了命令行程序外,腳本語(yǔ)言在其他的應(yīng)用程序中也身影頻現(xiàn),如圖形界面應(yīng)用、多媒體應(yīng)用、網(wǎng)絡(luò)應(yīng)用等。尤其是網(wǎng)絡(luò)應(yīng)用,成為滋生和繁榮腳本語(yǔ)言最肥沃的土壤。例如:Perl非常廣泛地用于網(wǎng)絡(luò)服務(wù)器端的CGI編程;PHP更是專為動(dòng)態(tài)網(wǎng)頁(yè)而設(shè)計(jì)的語(yǔ)言;Ruby雖與Java同歲,但真正開(kāi)始風(fēng)行得益于網(wǎng)絡(luò)應(yīng)用框架Ruby on
Rails的成功;至于Javascript,長(zhǎng)期被邊緣化為網(wǎng)頁(yè)設(shè)計(jì)人員的語(yǔ)言,是web2.0的新寵AJAX真正將其帶入程序員的視線。”
逗號(hào)有些好奇:“什么時(shí)候腳本語(yǔ)言變成了動(dòng)態(tài)語(yǔ)言呢?”
“不是所有的腳本語(yǔ)言都能稱作動(dòng)態(tài)語(yǔ)言的,盡管后者并無(wú)確切的定義。”冒號(hào)回答,“從用途上看,一個(gè)腳本語(yǔ)言如果不再局限于命令行工具和粘合工具,從專用語(yǔ)言發(fā)展為通用語(yǔ)言,并能勝任復(fù)雜的應(yīng)用開(kāi)發(fā),或許更有資格歸為動(dòng)態(tài)語(yǔ)言。”
句號(hào)發(fā)現(xiàn):“動(dòng)態(tài)語(yǔ)言似乎對(duì)字符處理都特別擅長(zhǎng)。”
冒號(hào)道:“腳本語(yǔ)言與一般程序一個(gè)不同之處是,它一般是面向字符而非數(shù)值的,因?yàn)樽址亲钔ㄓ玫慕涌冢冒l(fā)揮其粘合作用,而數(shù)值運(yùn)算對(duì)性能要求較高,多由核心程序來(lái)完成。動(dòng)態(tài)語(yǔ)言繼承了這個(gè)特點(diǎn),并且除了正則表達(dá)式(Regular
Expression)外,為字符串、數(shù)組、映射等常用結(jié)構(gòu)提供了豐富簡(jiǎn)潔的運(yùn)算,遠(yuǎn)比靜態(tài)語(yǔ)言依賴于庫(kù)(library)的方便得多。”
嘆號(hào)問(wèn):“我們清楚了腳本語(yǔ)言中‘腳本’的來(lái)歷,那動(dòng)態(tài)語(yǔ)言中‘動(dòng)態(tài)’又體現(xiàn)在何處呢?”
“問(wèn)得好!”冒號(hào)聞言,正中下懷,“從用法上看,動(dòng)態(tài)語(yǔ)言能在運(yùn)行中增加或改變數(shù)據(jù)結(jié)構(gòu)、函數(shù)定義、對(duì)象行為或指令流程等,具有典型的動(dòng)態(tài)特征。相比而言,靜態(tài)語(yǔ)言雖然也能實(shí)現(xiàn)同樣的效果,但既不方便也不自然。此外不容忽視的一點(diǎn)是,動(dòng)態(tài)語(yǔ)言大多是開(kāi)源的,其本身的發(fā)展也更具動(dòng)態(tài)性。”
引號(hào)非常注重理論:“動(dòng)態(tài)語(yǔ)言的語(yǔ)法特征有那些?”
“動(dòng)態(tài)語(yǔ)言秉承的一個(gè)理念是:優(yōu)化人的時(shí)間而不是機(jī)器的時(shí)間,因此為提高人的生產(chǎn)率而不惜犧牲部分的程序性能。”冒號(hào)講述著,“從語(yǔ)法上看,動(dòng)態(tài)語(yǔ)言對(duì)類型的要求一般不如靜態(tài)語(yǔ)言那么嚴(yán)格,代碼更加簡(jiǎn)潔自由,故而多為動(dòng)態(tài)類型的和弱類型的,天然支持泛型式編程。當(dāng)然這不是絕對(duì)的,比如Groovy也支持靜態(tài)類型,Python一般認(rèn)為是強(qiáng)類型的。大多數(shù)動(dòng)態(tài)語(yǔ)言支持eval函數(shù),能動(dòng)態(tài)執(zhí)行任意字符串形式的代碼,天然支持元編程。動(dòng)態(tài)語(yǔ)言很多還支持包括高階函數(shù)(high-order
function)和閉包(closure)等在內(nèi)的函數(shù)式編程。此外,大多動(dòng)態(tài)語(yǔ)言也支持對(duì)象式編程,如Python、Ruby、Perl 5、PHP
3等。”
句號(hào)補(bǔ)充道:“許多動(dòng)態(tài)語(yǔ)言還支持過(guò)程式編程和并發(fā)式編程,簡(jiǎn)直把主要的編程范式一網(wǎng)打盡了!”
“其實(shí)Python、Ruby和Groovy等還可以進(jìn)行切面式編程。”冒號(hào)進(jìn)一步指出,“而邏輯式編程語(yǔ)言的代表Prolog,同樣有動(dòng)態(tài)語(yǔ)言的特征。”
引號(hào)高興地看到:“前面講的八大范式無(wú)一漏網(wǎng)啊!”
嘆號(hào)較為感性:“靜態(tài)語(yǔ)言給人的感覺(jué)是沉穩(wěn)持重,而動(dòng)態(tài)語(yǔ)言則活潑輕快。如果同時(shí)用靜態(tài)語(yǔ)言和動(dòng)態(tài)語(yǔ)言編程,豈不培養(yǎng)出雙重人格?”
“程序員本就是雙重人格的。”冒號(hào)淡淡地說(shuō),“你總結(jié)得沒(méi)錯(cuò),兩類語(yǔ)言的風(fēng)格的確大相異趣:待靜態(tài)語(yǔ)言披盔戴甲、備馬抬槍之際,動(dòng)態(tài)語(yǔ)言已衣袂飄飄,長(zhǎng)劍出手了。當(dāng)然如果是應(yīng)付強(qiáng)敵的長(zhǎng)期作戰(zhàn),靜態(tài)語(yǔ)言還是有優(yōu)勢(shì)的。”
引號(hào)聽(tīng)聲辨音:“這意味著動(dòng)態(tài)語(yǔ)言不適用大型應(yīng)用開(kāi)發(fā)嗎?“
“這么說(shuō)未免有些武斷。”冒號(hào)并不同意,“誠(chéng)然,動(dòng)態(tài)語(yǔ)言的語(yǔ)法比較寬松,相對(duì)容易出錯(cuò)。但也有人辯稱,動(dòng)態(tài)語(yǔ)言的代碼量少于相應(yīng)的靜態(tài)語(yǔ)言,bug應(yīng)該更少。有人認(rèn)為動(dòng)態(tài)語(yǔ)言調(diào)試不如靜態(tài)語(yǔ)言方便,有人卻說(shuō)隨著IDE的日益強(qiáng)大,出錯(cuò)幾率和找錯(cuò)成本也在減少。談到運(yùn)行效率,動(dòng)態(tài)語(yǔ)言雖然多為解釋型語(yǔ)言(Interpreted
Language),但許多也提供了與Java類似的字節(jié)碼編譯(bytecode compilation)甚至JIT編譯(Just-in-time compilation)。動(dòng)態(tài)語(yǔ)言在某方面甚至還更勝一籌:譬如一個(gè)類的接口如果發(fā)生變動(dòng),在靜態(tài)語(yǔ)言中所有該類的子類必須重新編譯、連接,這在大型應(yīng)用中是非常耗時(shí)的,而動(dòng)態(tài)語(yǔ)言則大可不必,其實(shí)這不足為奇——在它眼里類本來(lái)就是能動(dòng)態(tài)改變的。”
逗號(hào)開(kāi)始擔(dān)憂起來(lái):“動(dòng)態(tài)語(yǔ)言優(yōu)點(diǎn)突出而弱點(diǎn)并不突出,這樣下去靜態(tài)語(yǔ)言還有市場(chǎng)嗎?”
冒號(hào)坦然道:“動(dòng)態(tài)語(yǔ)言小快靈的風(fēng)格的確吸引了越來(lái)越多人的注意,也漸漸走入靜態(tài)語(yǔ)言的世界。Java平臺(tái)和.Net平臺(tái)不僅為Ruby和Python等動(dòng)態(tài)語(yǔ)言鋪設(shè)了跑道,而且為培植諸如Groovy等動(dòng)態(tài)語(yǔ)言提供了土壤。同時(shí),Java和C#本身也融進(jìn)了越來(lái)越多的動(dòng)態(tài)特征。”
句號(hào)斷言:“靜態(tài)語(yǔ)言這種融合性以及內(nèi)在的安全性和高效性,決定了它不可能被動(dòng)態(tài)語(yǔ)言完全取代。”
“對(duì)!”冒號(hào)堅(jiān)定地說(shuō),“當(dāng)腳本語(yǔ)言穿上動(dòng)態(tài)語(yǔ)言的彩衣,昔日不起眼的毛毛蟲便羽化成碟,開(kāi)始飄舞在眾人追逐的目光之中。但靜態(tài)語(yǔ)言也絕不會(huì)淡出人們的視線,它如矯健的蒼鷹,依然有搏擊長(zhǎng)空的雄力。程序員只要保持嚴(yán)謹(jǐn)?shù)淖黠L(fēng)和開(kāi)放的心態(tài),既有穩(wěn)如泰山的馬步,又有一躍凌空的飛腿,靜如處子,動(dòng)如脫兔,如履平地般游走于高高的梅花樁上,絕無(wú)跌落之虞。”
一股豪情在眾人心中蕩漾開(kāi)來(lái)。
冒號(hào)看了看時(shí)間,斂起眼中精光:“關(guān)于動(dòng)態(tài)語(yǔ)言,我們簡(jiǎn)單談到這里,下課!”