C++和動態語言的泛型
cf 的回應 中提到了 polymorphism 和 generic 的看法。我的看法稍有不同。
我眼中的泛型(generic),若用非常簡化的方式來表達,就是不管型別,只看程式形式,亦即演算法。如稍候例舉的 max() 就是一種泛型演算法 (STL 也有一個同名的演算法)。我的說法和其他人的說法應該沒什麼不同。那麼多型(polymorphism)、泛型和樣板(templete)之間有什麼關係?端視程式語言的特性而定。
首先,多型(polymorphism)具有類別繼承的性質,是個體導向(OO)的概念,但泛型(generic) 不是個體導向概念。我們同樣可以在只有基礎資料型態,沒有繼承觀念的環境中使用泛型設計,只是彈性就低多了。在個體導向語言中,藉由類別繼承與動態連結的能力,才能充分發揮泛型設計的優點。
其次,說到泛型與樣板的關係。C/C++語言要求明確的型別,所以 C/C++ 如果沒有代換型別的能力,就實現不了泛型。C++ 樣板原意是「型別參數化」(這是C++之父最初提出樣板概念時的說法),意義表達很精確。所以C++要談泛型,要先從樣板著手。雖然樣板是在個體導向功能之後才加入 C++ 的功能,但它也可以先於個體導向之前存在。例如函數樣板(Function template) 就可以這樣使用。我們可以按傳統 C 那樣設計程式,但用函數樣板幫助程序員減少編寫重複的相似程式碼。精確地說,是將程式碼的產生工作由程序員轉給編譯器。
若從泛型與編譯器的關係來看,沒有編譯器的動態語言環境就不需要樣板的功能。See also: Ruby versus Smalltalk versus .... 所以 cf 說「我還沒有在其他的地方看過」是很合理的,因為 Ruby, Perl, JavaScript 等動態環境中用不著。故不能以「是否具備樣板功能」判定程式語言的泛型支援能力。
C 和樣板
以下範例,使用 C 的方式設計程式,且無視個體導向能力,僅運用樣板進行泛型設計。
使用樣板前
下列範例用到了 C99 後加入的 Overload 功能。如果不使用 Overload ,那麼函數名稱通常要分別寫成: max_i(), max_f(), max_c(), print_i(), print_f(), print_c()
等相似的名稱。那是更為傳統的寫法。
使用 Template 後
一個只用基本資料型態的 C 程式,在使用樣板進行泛型設計之後,程式碼內容成功地減肥了。
動態語言
我個人並非以語法區分動態語言和靜態語言,參閱《如何區分動態語言和靜態語言》。傳統上沒有編譯器的程式語言,如 Ruby, JavaScript, Perl, Python 等,我們目前慣稱為動態語言。這些程式語言的語意通常就有泛型概念。如下列 JavaScript 程式中的 max()
,和 C++ 樣板相比,沒什麼不同。
從語法與語意看,動態語言的語法其實就是泛型的。它們只是因為沒有編譯動作,而不得不 late binding 。並不得不仰賴其他的處理策略。參考《Use C/C++ with Dynamic Languages is Easier Than Pure C++》及《個體之間協議互動行為的多種形式》。
動態語言之中,有些有編譯器的實作品,例如 JavaScript 。在 .Net Framework 中有一個 JScript compiler 。然而這些編譯器的實作功能殘缺不齊。它們充其量只能檢查語法,而不會檢查型態。如果廠商真的要實作 JavaScript 的編譯器,那麼應該要如同 C/C++ 那樣附帶一個宣告文件,或者是引用 TDD 概念要求附帶一個 Test case ,供編譯器檢查型態才對。
樂多舊回應