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