編程范式巡禮第一季 三大基石
最近迷上了一些哲史類書籍,回望過去、放眼未來,往往沉浸在其思維之美中無法自拔。計算機編程是一門非常年輕的學科,沉淀不足也是年輕的一個側面,在編程領域,有足夠思想深度的作品并不多。這本書的作者老冒我覺得就是一個有深度的人。這周開始,給大家講一下他的好書《冒號課堂》。
編程范式是什么?
在年底的各大媒體的展望中,我發現編程能力已經悄悄然占據了比較重要的位置,被認為是一種影響未來的能力。編程從宏觀上講就是操作計算機工作的方法,而從微觀上講是尋求一種機制,將指定的輸入轉換為指定的輸出。
但是為什么編程能力是難練成的呢?我覺得最大的問題大于人和計算機的思維方式是不同的,人的思維追求簡單,而計算機的思維追求復雜。在這之間有一個巨大的鴻溝,其難點是使用人的思維來翻譯計算機思維。
編程范式就是這種翻譯方法,它是程序王國的世界觀和方法論,也許是破解編程這門學科的鑰匙。
命令范式
我們需要從最古老的范式開始,也是編程世界的第一性原理。
其世界觀是:程序是由若干行動指令組成的有序列表。其方法論是:用變量來存儲數據,用語句來執行指令。
這個是由目前計算機的起源,也就是馮·諾依曼機的設計來決定的,也是所有計算機技術的基礎。機器語言就是這樣設計的,也就是說這是計算機唯一可以直接理解的東西。
那是不是可以有其他的機器語言呢?理論上當然是可以的,在早期也是存在的,但是都未有流行。在命令范式中有一個非常重要的推論:
任何程序都可以用順序、選擇和循環3種基本控制結構來表示。
這也是我們目前絕大多數語言的實現方式,是日常最為常見的編程方法。下面是一個范例:
function getData(col){
var results=[];
for(var i =0; i < n ; i++){
if(col[i]&&col[i].data){
results.push(col[i].data);
}
}
return results;
}
大家可以試一下用語言把這段代碼進行一下解釋,我也找不少同事做過實驗,就算是資深程序員解讀的也不完全相同,這也說明了命令范式和人的思維方式有所不同,會不太容易理解,如何解決,就需要新的范式了。
函數范式
上面的那段代碼,到底是哪里不容易理解?應該來說是細節的描述過多,因為人的思維喜歡簡單,而細節是復雜的。所以我們需要一種不一樣的描述方法。
命令式編程是行動導向的,算法是顯性而目標是隱性的;聲明式編程是目標驅動的,目前是顯性而算法是隱性的。
命令式編程有幾種不同的流派,其中函數范式是現在最為流行的。
在函數范式中,函數是程序的核心,是頭等公民。
這個說法還是過于抽象,我們從上面的例子出發。這段代碼中,最讓人難以理解的應該是for..下面的這段內容,也就是循環段落。在命令范式的三大結構中,我們會發現唯有循環結構是人在日常表達中不會使用的,這也是計算機復雜性的最重要表現。在聲明式編程的世界觀里,我們就需要把這段代碼藏起來。
但是如果只是采用命令范式中"語句+變量"的表達方式,大概能改進成如下代碼,是好了一點,但是還是挺讓人費解的。
function getData(col){
var results=[];
doSelect();
return results;
}
function doSelect(){
results = loop(
f-> { if(col[i]&&col[i].data){
results.push(col[i].data);
}});
}
}
這個時候,我們就需要對范式進行一下改進了。其實只是一個小改動,擴展一下"變量"的語義:
語句+變量 --> 語句+變量/函數
我想這個也是函數范式名稱的由來,這么做的效果立竿見影,"變量"復雜了,那么"語句"就能變的簡單而且通用。我們可以把上面這段代碼改進為如下:
function getData(col){
return col
.filter(item=>item&&item.data)
.map(item=>item.data);
}
是不是可讀性一下子強了很多,改變只是源于一個小小的思維上的轉變。以此為基礎,發展出了許許多多強大的方法和工具,給碼農們提供了強大的支持。讀到這里,我其實是有點感動的,這就是思維的力量。
對象范式
讓我們繼續,函數范式把程序的可理解性提升了一截,那思維探索就停止了么,當然不會。
命令范式的代碼在理解性方面還有其他的問題么?有!
和日常的語言相比,程序的表示總是讓人覺得有些別扭。以第一行代碼為例。
function getData(col)
我們直接讀是"獲取數據 列",挺奇怪的吧。舉一個日常表達的例子會更形象點,程序的表達類似這樣:
吃 牛 草
看出問題了吧,就是缺少主語。我們日常的表達中,主語是個不可缺少的內容,但是在計算機語言中偏偏就沒有。那這句話如何表達才能清晰呢,挺簡單的,換下順序增加主語就可以。
牛 吃 草
在程序世界里,給主語起了個名字,叫Object,基于此,誕生了對象范式。這里有一個非常混淆的地方,就是Object并不是主語的意思,而且意思恰恰相反。我覺得這也是對象范式這個概念,始終讓大家非常困惑的一個原因。這里只是給一個簡單化的理解方法。
語言表達的改進,帶來的不僅僅是理解的提升,更大的改變是可以將我們理解正常世界的方法用在理解計算機世界之上,這個化學反應是巨大的。這里舉一個簡單的例子,就是信息壓縮。
牛 吃 草
牛 喝 水
這兩句話,我們自然會想到一個壓縮的方法:
牛 {
吃();
喝水();
}
進一步,又出現了一種新動物,我們又會想到一種壓縮:
羊 {
吃();
喝水();
}
-->
哺乳動物 {
吃();
喝水();
}
這就是OO中的封裝和繼承的由來,追求的是簡單,源于日常理解世界的方法。為什么現在對象范式這么流行,我覺得重要的原因是其更符合日常思維,更具有易用性。
小結
今天主要是介紹了《冒號課堂》中講到的最為基礎的三個范式,從一個小小的改動開始,撬動了一個大大的世界,編程領域博大精深,這只是入門,后面還有更好玩、更有趣的范式等著我們,下次再繼續探索。
前幾天聽到Google的人工智能專家李飛飛的演講,講到傳統的學習,是用人認識知識,然后再讓機器學習,但是機器學習不一樣,是把認知知識這一層省掉了,直接讓機器進行學習。這是一種思維上的大躍進,說實話我還不能完全理解,但有點可以確定的是,思維上的提升已經刻不容緩,加油。