如果我告訴大家,這篇文章出自一個只有20歲的小伙,我想很多人都會感到吃驚,也談編程改革
。至少我是吃了一驚,因為這篇文章涉及到主題聽起來是很有深度的,我本人在20歲時幾乎想都不會想這些事情,更別說研究了。但又過了這么多年,不知國內(nèi)的青年們有沒有追趕上西方的步伐,也能出現(xiàn)幾個這樣看起來很有編程天分的人?
我最近看到了《編程改革 》這篇文章,里面的內(nèi)容討論到了我們的編程中存在的一個最根本的問題。我同意作者的觀點,但我感覺很多的評論并沒有理解他說的問題,所以,我打算用另外一種方式來說明一下。
我從事編程已經(jīng)很久,主要是因為我癡迷于解決難題。我非常喜歡研究編程語言,一部分原因是作為一個程序員,我本身是被它們包圍,同時也是因為語言是讓我們成為人類的一個重要因素。
我享受編程這種職業(yè),我喜歡這種能夠從無到有創(chuàng)造出神奇東西的能力,但同時也對我們每天需要處理的這些事情感到失望。下面就是原因:
面向過程 vs 面向結(jié)果的編程
鏈?zhǔn)?Concatenative),函數(shù)式,面向?qū)ο蟮,邏輯?mdash;—不管你選擇何種編程模式,他們?nèi)加邢嗤膯栴}:它們要求你去描述如何做,而不是做什么。在你能叫得出名字的任何一種語言里,程序是一個對能計算出你想要的東西的處理過程的描述,而不是一個對你想要的東西的描述。誠然,一些語言會比另外一些語言更具有陳述性,但這跟“陳述式 vs 命令式”的問題并不相關(guān)——能夠陳述式的表達一個過程的語言仍然是面向過程的,而不是面向結(jié)果的。
這才是癥結(jié)所在。作為程序員,我們用代碼解決問題。我們應(yīng)該能夠用代碼來表達我們想要的結(jié)果,而不是想要達到這種結(jié)果需要的過程。
作者提出的是一種“約束滿足(constraint satisfaction)”式的編程方式。給出一系列的約束條件,你就能夠從中推導(dǎo)出一種能夠滿足它們的算法。當(dāng)然,我們必然的會擔(dān)心編譯器推導(dǎo)出的這種算法的正確性和各種性能指標(biāo);是否本來可以做到Θ(nlogn)級別的,它編譯器卻給出了Θ(n!)?這是一個很合理的擔(dān)心,但它只是跟你的描述是否嚴(yán)謹(jǐn)有關(guān)。
你也知道,對于一個特定的算法,我們可以通過很多種不同的但卻等效的約束集合來定義。從中進行優(yōu)選,我們有信心相信,編譯器能夠推導(dǎo)出我們想要的結(jié)果。我們的工作應(yīng)該變成把需求用有效的約束正確的表達出來,讓編譯器去滿足這些約束。
我知道這聽起來很枯燥,我最初也是這種神奇的編程方式的懷疑者。但想一想:任何值得一提的程序其實都是這樣實現(xiàn)的,只是它們是以一種效率低的容易出錯的手工方式。一個程序員拿到一些需求,先整理它們,然后辛苦的把它們轉(zhuǎn)換成一種合適的處理過程。我們可以省掉這其中的一個步驟。
一個現(xiàn)實的例子
假設(shè)你在編寫一個游戲。在你的待完成任務(wù)清單上的第一個要實現(xiàn)的事情是:通過方向鍵,讓一個游戲角色可以在屏幕上四處移動。在任何一種語言里,你不得不構(gòu)造出一大堆不相干的基礎(chǔ)構(gòu)件來實現(xiàn)這個操作:
把一個連續(xù)的物理動作轉(zhuǎn)變成一系列相互分離的圖像
處理幀頻,幀渲染,幀沖突等問題
管理各種活物體和死物體的狀態(tài)和資源使用問題
所有的這些折騰實際上跟我們想要的東西是沒有關(guān)系的。
很多時候,這些工作已經(jīng)由某個程序庫提供完成了,但這表明一個事實:需要有人去做這些麻煩事。相比較,功能應(yīng)對式編程很適合做這種事情。你可以準(zhǔn)確的表達出你的意思:
這個游戲角色的特征包括:
某位置的加速度( ax ,ay )
某位置的速度( vx ,vy )
位置( x,y )
物體的大小( w,h )
當(dāng)方向鍵按下時,加速度改變:
左:( 1, 0 )
右:( +1, 0 )
上:( 0, 1 )
下:( 0, +1 )
隨著時間的相互作用:
加速后的速度
運動后的位置
在( px ,py )處像素點的顏色是:
如果( px ,py )在物體的( x,y,x+w,y+h )范圍內(nèi):
顯示物體( pxx,pyy )處的顏色
否則:
顯示背景色
這是一個完備的,完全陳述式的,完全面向結(jié)果的對一個程序的描述,
管理資料
《也談編程改革》(http://www.ishadingyu.com)。這些描述都是跟你目前要實現(xiàn)的目標(biāo)任務(wù)是直接相關(guān)的,每個描述都是一種約束,在一個RRP(這是一個簡單的約束求解程序,根據(jù)實時狀態(tài)推導(dǎo)解決方案)系統(tǒng)里,你可以按意愿添加或去除這種約束。何去何從?
Prolog語言就是一個以約束為基礎(chǔ)的編程方式的樣本,而Haskell語言里的FRP庫表現(xiàn)的更好,但這些跟我們能夠做到的比起來更像玩具。
沒有任何理由去說我們不能實現(xiàn)一種更好、更面向結(jié)果的語言,或者把目前存在的面向過程的語言變得更像面向結(jié)果,為這種編程模式賦予更大的能力。參考一下Swym這篇文章,里面提到的關(guān)鍵字etc
就能讓你實現(xiàn)類似的事情:
List.byPairs: [[.1st, .2nd], [.3rd, .4th], etc];byPairs[1..10] == [[1,2],[3,4],[5,6],[7,8],[9,10]];
當(dāng)你提供給它一些不同的參數(shù),它能做出更智能的事情:
[1,2,3].byPairs == [[1,2]];[1,2].byPairs == [[1,2]];[1].byPairs == [];
這真是讓人興奮,但事情并非到此為止,我們可以把它做的更好。我們實現(xiàn)更多的類似etc
和each
這樣的”魔法“操作符,可以讓一種命令式的語言看起來更像面向結(jié)果的語言。
有一段時間,我使用一個叫做Prog的語言工作。你可以把它按照普通的命令式的語言進行編程。但它附帶有一些特殊的語言特征。這些神奇的語言特征是通過“radioactive types”實現(xiàn)的,它們是一種中間態(tài)的語言行為特征,只能在解釋器分析期運行,過了此階段,它們就蛻化成普通的語法。舉個例子,對一個枚舉數(shù)據(jù)結(jié)構(gòu)進行各種操作,可以這樣寫:
which (1..10) > 5 == (6, 7, 8, 9, 10);each (1..5) ** 2 == (1, 4, 9, 16, 25);each (1..3) * each (1..3) == ((1,2,3), (2,4,6), (3,6,9));
在讀了那篇文章和之后的評論后,我激起了再次使用Prog語言的想法——如果有時間的話。我希望能看到更多的關(guān)于這方面的文章出現(xiàn)。目前開發(fā)的一個具有相似理念的項目消耗了我大量的精力,那是一個能讓非程序員用戶制作出專業(yè)質(zhì)量的交互式媒體的工具。如果你不知道如何編程,你在創(chuàng)建游戲或其他媒體應(yīng)用時會受到阻礙。所有的這些幫助創(chuàng)造性的軟件基本上可以歸為兩類:
功能可憐的有限
復(fù)雜的沒法用
所以,利用研究人們能如何更好的解決問題,我開發(fā)了這樣一個工具,能讓那些心里有想法、但沒能力把它變成現(xiàn)實的人解決他們的問題。目前為止,我們還有繼續(xù)努力。如果你是一個程序員,受到這篇的啟發(fā),想?yún)⑴c做一些事情,請聯(lián)系我evincarofautumn@gmail.com,我會告訴你如何參與進來。