Lambdafunction

再讓我們回到高階語言的主題,讀者心中可能會存有一個疑問,為何要區別編譯式語言與直譯式語言呢?直譯式語言難道無法編譯成執行檔,直接執行嗎?這個問題的答案是,要看你對直接執行的定義是甚麼?如果直接執行的方法,只不過是將一個直譯器直接放入執行檔當中,最後還是用那個直譯器對對程式進行直譯的動作,那麼,當然所有的直譯式語言都可以變成執行檔,因此,剩下的問題就變成了,到底執行檔當中包含了多少解譯的動作在裡面呢?

要解釋何謂解譯式的動作,很難在不用程式範例的情況下訴說清楚,現在,該是讓我們看看實際程式範例的好時機了,我們將使用標準的編譯式語言 C與解譯式語言 Python 來說明編譯與解譯的不同點。由於本書的讀者通常已對 C 語言相當熟悉,所以我們會將焦點放在一些 Python 上輕易就可以做到,但是 C 語言卻難以做到的範例上。其中最能說明兩者的差異的,莫過於 lambda 這個由 Lisp 當中傳承下來,既詭異卻又功能強大的語法。

Python 中的lambda 關鍵字所代表的,是一個延遲計算的概念,lambda 所代表的程式區塊,暫時不會被執行,於是,該計算被延遲了,直到接收該 lambda 的程式,要求其執行時,lambda 才會被執行。透過 lambda 的概念,我們可以將 lambda程式區塊傳來傳去,然後在適當的時候執行之,如此,程式可以當成參數,傳給另一個程式,然後在另一個程式當中,再利用該程式進行某些嵌入式的功能,也就是可以將 lambda 程式透過參數的方式嵌入到另一個程式當中執行。這在程式當中是一個強而有力的概念。舉例而言,在範例 7.4當中,我們就將 lambda x:x%3==0 這個程式區塊,直接傳給 filter 函數,該 lambda 區塊會在 filter 這個函數當中才被展開執行,於是,就能讓 filter 函數根據 lambda 參數進行過濾的功能,於是 foo 陣列在經 filter 過濾後只留下 3 的倍數。這樣的程式在 C 語言當中是很難做到的。因為,直譯式語言可以在需要的時候,動態的解譯 lambda 算式,因為直譯器隨時都在旁邊等著 (Stand by)。

範例 7.4 Python 中的 Lambda 表達式的使用。

>>> foo = [2, 18, 9, 22, 17, 24, 8, 12, 27]
>>> 
>>> print filter(lambda x: x % 3 == 0, foo)
[18, 9, 24, 12, 27]
>>> 
>>> print map(lambda x: x * 2 + 10, foo)
[14, 46, 28, 54, 44, 58, 26, 34, 64]
>>> 
>>> print reduce(lambda x, y: x + y, foo)
139

同樣的,範例 7.4當中利用 map 函數,將 foo 陣列的每個元素平方後,再傳給 print 函數印出,雖然 C 語言也能做到計算陣列二次方的事情,但是卻很難寫出像 map 這樣強有力的函數,因為 C 語言的設計上沒有給直譯器留下空間,難以動態的解譯運算式。

最後,範例 7.4中的 reduce 更用一行就算出了 foo 陣列的總合,這些範例應該足以讓讀者體會到直譯式語言的優點,以及 lambda 語法的強大能力。

編譯式語言與直譯式語言還有一個相當大的不同點,通常編譯式語言對於形態的檢查較為嚴格,但在直譯式語言上則不注重形態的檢查,但這只是個常見的現象,並不代表編譯式語言一定要進行型態檢查,或者解釋式語言一定不能做形態檢查。

傳統上,型態檢查可以幫助程式師減少程式的錯誤,而編譯式語言在編譯時可以順便進行全面性的型態確認動作,因此,像是 C, C++ 等強型態 (Strong Typed) 的語言通常會採用編譯的方式。而 JavaScript、LISP 等弱型態 (Weak Typed) 的語言則會採用直譯式的方式。

弱型態的語言由於不需宣告形態,因此,其程式通常較短,語法較為簡捷,這使得程式設計師可以少打很多字,寫程式的效率也會較高。但是,當編譯器試圖將弱型態語言編譯成執行檔時,由於缺乏型態資訊,因此,即使編譯為執行檔,通常其中仍然包含某些解譯動作,以便進行形態、函數或物件上的對映。像是 Ruby 語言即採用極度的弱型態語法,稱為 duck typing (鴨子型態),這個詞彙的來源是一句有點俏皮的話 If it walks like a duck and quacks like a duck, I would call it a duck『如果他走路像鴨子、聲音也像鴨子、那我們就叫他鴨子吧』。這樣的邏輯顯示了弱型態語言的特徵,將型態的問題延後到執行時才處理。

由於解譯時才處理型態的繫結問題,這使得弱型態語言很難編譯成執行檔,甚至,對於某些特殊的語法,若要進行編譯,必須將整個直譯器都放入執行檔才有可能。例如,Foxpro 這個語言,允許將任何一段字串傳入後,展開貼入到程式當中,這樣的功能就很難進行編譯,除非將整個直譯器都嵌入到執行檔當中,才有可能處理這樣的語法。因此,這樣的語言似乎就註定與直譯器綁在一起了。

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License