Stephen Covey的《The 7 Habits of Highly Effective People》一書中,有一個關于paradigm shift(範式轉移)的小故事:
軍艦在霧天執行緊急任務,突然發現船的一側有來自其他處的照明燈光,艦長要求對方轉向,否則有兩船相撞的危險。可惜對方堅持讓軍艦轉向,互不相讓。艦長甚至亮出了自己的軍銜相挾“我是海軍上校,這裡是軍艦!”。而最後的結果,卻是本來态度強硬的軍艦選擇了轉向,因為對方的答複是:“我是二等兵,這裡是燈塔!”
雖然關于範式的準确定義,說法有很多,我也不打算深究并記錄進筆記,但從這個小故事中可見,範式是我們思考的一種模式,正确的模式會讓我們更加接近真實,更容易達到特定的目的,而錯誤的模式則會讓我們事倍功半。
我們學習程式設計都是從具體的程式設計語言,如C,C++,java等開始的。是以一開始往往沒有深入接觸programming paradigms(程式設計範式)的概念,最多是簡單地介紹一句“C是面向對象的程式設計語言“,再多問一句,得到的回答也隻是”以後你學到C++就會發現它是面向對象的程式設計語言“。其實這裡面有很多深刻的話題可以談論,值得深入地考慮。譬如說,一門語言和程式設計範式的關系,并非是”C是面向對象的程式設計語言“的關系那麼簡單,一門程式設計語言和程式設計範式其實沒有綁定或者專屬的關系,一個是文法語義層面的,另外一個是運算算法層面的,一般說某種程式設計語言支援某種程式設計範式可能比較恰當,甚至有一些語言還支援混合程式設計範式。其實C也能支援一些面向對象的因素,隻是不如其他按照OOP(Object-oriented Programming)範式開發的語言支援得完善(繼承、多态、重載?),是以說”C是面向對象的程式設計語言“也能讓人接受,而C++也能做到純粹的面向過程,但作為C的超集,其最大的不同就是對OOP的支援,故把它稱為面向對象的程式設計語言。
程式設計範式的分類也有很多種,除了上面的分類,還有結構化、非結構化等分類。我想最主要的分類還是按照model of computation(計算模型)來分,一般而言有四類範式:
- 基于Turing Machine(圖靈機)的Imperative Programming(指令程式設計範式);
- 基于Turing Machine(圖靈機)的Object-oriented Programming(面向對象的程式設計範式);
- 基于λ-calculus(λ演算)的Functional Programming(函數程式設計範式);
- 基于First-order logic(一階邏輯)的Logic Programming(邏輯程式設計範式)。
關于程式設計範式的分類也十分複雜,想深究的可以留意下圖:
指令式程式設計範式主要是”發号施令“形式的程式設計模式,程式按照一定的順序執行各條指令,程式員需要具體到程式如何去做的層次。而與之相對的是Declarative Programming(聲明程式設計範式),隻須程式員指明做什麼的層次,而無需深入具體如何做的層次,函數程式設計範式和邏輯程式設計範式均是聲明程式設計範式,下文再加以介紹。這種”發号施令“式的程式設計範式貼合硬體的執行,是最早的程式設計範式,一環接一環,環環相扣地執行,是以任何一個細節出錯可能都會導緻整個程式的錯誤。為了滿足Low coupling(低耦合), High cohesion(高内聚)的設計模式,面向過程式的程式設計範式(也是指令式程式設計範式一種)漸漸發展起來,其最大特色就是把所有程式分化為逐個子函數或者子程式(C裡面幾乎所有有執行意義的代碼都需要在某個函數裡,如main函數等)。而且它引入的變量、函數傳回值等機制,使整個程式高度子產品化,把狀态的改變對應到不同的子函數之中去。因而也有很強的Side Effect。這種程式設計範式和圖靈機的設想很貼合,也是程式設計方面比較主流的範式(軟硬體條件共同決定的)。
随之而來發展出了面向對象的程式設計範式,如果某種語言支援這種範式,意味着支援繼承、多态、抽象、封裝、重載等等功能。從面向過程式的程式設計範式到面向對象式的程式設計,程式設計範式有了一個新的提高,因為面向對象的程式設計範式提出了類、對象、執行個體等概念,一方面貼合我們的思維,另外一方面也為代碼的重複利用做了很大貢獻。相比于指令式程式設計,其子產品化更為淋漓盡緻,子函數的角色被模糊融合到各個類、對象中去,且各個類、對象又有獨自的資料和方法,能夠互相收發資訊,這樣寫出來的程式不如傳統的指令程式設計範式般冗長,簡潔明了。但從實際上來說依然是圖靈機的模型。
以上兩種程式設計範式應該最為程式員所熟悉,占據了大半壁江山。
函數程式設計範式則摒棄了看重資料和狀态的計算模型,也就摒棄了副作用。找眼于函數本身,而非執行的過程的資料和狀态的處理。Haskell是其代表語言之一。函數程式設計範式在商業上應用不如在研究領域的應用廣泛和突出。愛大曾在70年代發明了ML函數式程式設計語言,也在這個領域有不少的貢獻,至今Functional Programming都還是資訊學院的大一必修課,讓很多新生頭疼。下面看一個斐波那契數列的例子:
C++:
#include <iostream>
// Fibonacci numbers, imperative style
int fibonacci(int iterations)
{
int first = 0, second = 1;// seed values
for (int i = 0; i< iterations-1;++i){
int sum = first + second;
first = second;
second = sum;
}
return first;
}
int main()
{
std::cout<< fibonacci(10)<<"\n";
return 0;
}
Haskell:
fibonacci2 =0:1:zipwith(+) fibonacci2(tail fibonacci2)
就那麼簡明,是不是有耳目一新的感覺呢?
就那麼簡明,是不是有耳目一新的感覺呢?
邏輯程式設計範式和函數程式設計範式相類似,但也不同,它所确立的簡單規則就是:事實+規則=結果。這種按照邏輯規律去産生結果的範式,更大程度地把程式從對資料的進行中解放出來。是一個從更抽象層次考慮問題的範式。代表語言是Prolog,愛大是Prolog的發源地。邏輯程式設計範式被普遍運用于人工智能領域。可以看一下面一段簡單例子:
father_child(tom, sally).
father_child(tom, erica).
?-sibling(sally,erica).
若sally的父親是tom,erica的父親是tom,那麼sally和erica是兄弟姐妹關系麼?答案是Yes。
總之不同的程式設計範式在算法設計、程式設計風格上都有很大的不同,它們各有特點,也各自運用于不同的領域,對它們的研究會随着軟硬體的發展而持續下去。
一些相關的參考資料:
維基百科(參考了部分代碼)
Programming Paradigms for Dummies: What Every Programmer Should Know (Peter Von Roy,Université catholique de Louvain à Louvain-la-Neuve,本文分類圖來源 )
Programming paradigms(Jerry Cain,Standford University)