代碼是否存在「美感」?
源文件頁面下的代碼是否存在美感?是否可能成為一種藝術形式?
這個問題想討論美的客體特徵:到底什麼是審美的基礎?什麼樣的事物才有被審美的可能?
結構的美就跟數學公式的美一樣。你看到一句代碼,你把不同的部分換成不同的東西,它的結構沒有變化,但是他的意義可以千差萬別。就像Haskell的Monad,做出來就很漂亮。當然Monad有他自己的缺點,我就不談了。相應的還有F#的computation expression。C#的yield return和async await算是應用的很巧妙的一個例子(也就是下面說的沒有噪音),但卻不存在結構的美,因為你不能替換裡面的零件。當然結構還有設計模式,但是設計模式看起來就像補丁,不美,遠遠不如這個:名著類 - ajoo
上面是第一種美。第二種美,就是代碼毫無噪音。噪音是什麼呢,我舉一個例子。下面兩段代碼描述的是同一類東西,你說哪個好?
https://gac.codeplex.com/sourceControl/latest#Common/Source/Parsing/Json/ParsingJson_Parser.parser.txt
https://gac.codeplex.com/sourceControl/latest#Common/Source/Parsing/ParsingDefinitions_CreateParserDefinition.cpp
打開讓屁股看一看都知道是第一個好了。這就是沒有噪音和有噪音的區別。當然我這段C++還是算好的,你要是把他的意義直接展開成代碼,也就是一個遞歸向下的語法分析器(不知道什麼是遞歸向下的看這篇教程: 如何手寫語法分析器),那就別想看懂了,只能靠作者好心寫注釋了。
如果你寫的一個程序,可以同時擁有結構優美和代碼沒有噪音這兩種優點,那就是一個漂亮的程序了,任何時候都應該拿出來炫耀。
舉三個例子說明代碼中可能存在的美,其中兩個來自《代碼之美》這本書的第一章和最後一章,第三個例子來自 C++ 之父的一次技術講座。1. 正則表達式匹配程序原文中的需求是寫一個精簡的正則表達式匹配程序,要求能夠匹配:c 任意字幕. 任意單個字元^ 字元串開頭
$ 字元串結尾
* 零個或者多個前一個字元/* match: search for regexp anywhere in text */
int match(char *regexp, char *text)
{
if (regexp[0] == ^)
return matchhere(regexp+1, text);
do { /* must look even if string is empty */
if (matchhere(regexp, text))
return 1;
} while (*text++ != );
return 0;
}
/* matchhere: search for regexp at beginning of text */
int matchhere(char *regexp, char *text)
{
if (regexp[0] == )
return 1;
if (regexp[1] == *)
return matchstar(regexp[0], regexp+2, text);
if (regexp[0] == $ regexp[1] == )
return *text == ;
if (*text!= (regexp[0]==. || regexp[0]==*text))
return matchhere(regexp+1, text+1);
return 0;
}
/* matchstar: search for c*regexp at beginning of text */
int matchstar(int c, char *regexp, char *text)
{
do { /* * matches zero or more instances */
if (matchhere(regexp, text))
return 1;
} while (*text != (*text++ == c || c == .));
return 0;
}
Bjarne Stroupstrup 舉了這樣一個真實的例子,怎樣重構下面的代碼:
void drag_item_to(Vector v, Vector::iterator source, Coordinate p)
{
// find the insertion point
Vector::iterator dest = find_if(v.begin(), v.end(), contains(p));
if (source &< dest) rotate(source, source+1, dest); // from before insertion point else rotate(dest, source, source+1); // from after insertion point }
即使你很熟悉 rotate ,第一眼你不會想到這幾行代碼是作什麼用。它是 vector 內部的移動操作,將 source 插到 dest
之前。數據類型和判斷都是針對特定問題的。如果將它重構成泛型,這些特定的類型會被泛化,更重要的這個操作會將以基本、正交的操作表達出來,而不是各種條件判斷和循環。這好比提取特徵向量。
重構的代碼如下:
template &< typename Iter, typename Predicate&>
pair&
// move elements for which pred() is true to the insertion point p
{
return make_pair(
stable_partition(first, p, !bind(pred, _1)), // from before insertion point
stable_partition(p, last, bind(pred, _1)) // from after insertion point
);
}
在線性隨機訪問空間 [i, j) 上將滿足條件 p 的元素插入到位置 k:insert(i,j,k,p),等價於將 [i, j) 分成 [i, k) 和 [k, j) 兩個空間,分別用條件 !p 和 p 進行 partition 操作:
insert(i, j, k, p) = partition(i, k, !p) ∪ partition(k, j, p)很明顯,partition 更本質,也更通用,更重要的是它用(重複的)自己定義了 insert,insert
被分解成為兩個相同結構的子問題,這個分解像不像是發現問題的遞歸子結構?!將 insert 以 partition
的方式表達出來是不是顯得更接近問題的實質,而且還更漂亮,更少的代碼和 bug。C++ (STL) 將這種「子結構」平台搭建好,但需要
paradigm shift,才能發現其中之美。實踐意義上,泛化就像設計模式一樣,除了提供的技巧之外,更重要的是指明了重構的目標:將代碼用標準演算法組裝出來。
3. 判斷三點是否共線的程序
判斷三點是否共線,看起來是很簡單的問題 —— 你應該看看那本書,事情並沒有你看起來這麼簡單,比如使用斜率的話,你要考慮垂直的特殊情況;如果用三角形兩邊和大於第三邊,會涉及到開方運算,無理數精度有限會導致問題。最後,作者找到了完美的判斷方法:計算三角形的面積,如果為零,則三點共線。當作者給出最後完美的程序時,你只能嘆服:(defun area-collinear (px py qx qy rx ry)
(= (* (- px rx) (- qy ry))
(* (- qx rx) (- py ry))))
它基於計算面積的矢量公式。
作者說存在傳說中的一本書,裡面記錄了最完美的數學證明,而他試圖尋找這種能記錄在類似書中的代碼。無獨與偶,頑固地認為存在這種書的並不只這篇文章的作者,大名鼎鼎的 Knuth 在他的 literate programming 中寫到:對他們,對那些執著地尋找最完美的代碼的程序員們,在另一種語言,另一空間,有大師福樓拜遙相呼應:我強烈地愛上了這套新方法,對過去寫的每一個程序,我都抑制不住地想將它們「文藝」化。我發現自己忍不住去編那些布置給學生助教們的作業程序,為何?因為對我而言,我最終寫出了那些程序,這就是它們本該被寫成的樣子。新方法促使我寫出比以往更可讀、也更好的程序。
他們深刻理解到符號系統最具價值的地方在其精確,這種極致追求劈開了普通人與真正的大師之間難以逾越的鴻溝,這是「差不多就可以」與「完全無法企及」之間的光年距離。這種美就像你初次遇見完全對的人:此生不再作它想。不論一個作家所要描寫的東西是什麼,只有一個名詞可供他使用,只有一個動詞能使對象生動,只有一個形容詞能使對象的性質鮮明。因此就得用心去尋找,直至找到那一個名詞、那一個動詞和那一個形容詞。
總結
毫無疑問,上述例子證明了代碼的美感是存在的,但這種美,就像欣賞任何其他嚴肅的藝術作品一樣,需要你理智上的能力和付出,而不僅僅是直觀上的感受。更私人的看法就是,程序是最接近巴赫音樂的藝術。它們都是在形式上試圖去挑戰難以企及的內容,前者用符號接近人的理性,而後者用另一種符號向神祈禱傾訴。簡言之,在我看來,代碼之美存在於簡潔優雅的數學真理和自我遞歸的完美重複中。而這一切,大自然早已知道,並以各種令人驚狂的方式藏在一滴水、或者一片樹葉當中。只有看的懂的人才能了解其中的美,包括結構美。你讓一個沒接觸過編程的人看代碼,TA只會說「我看不懂,真噁心,我無法理解你的藝術品」,親測,給女朋友看過,原話。
是有美感的。當我第一次閱讀一個開源庫的源碼時,就情不自禁地感覺到好美好優雅,有點像藝術品。而最近一直看公司內部的代碼,跟翔一樣……
代碼的美,我沒有太在意過。但是代碼的清晰易讀性是很顯然的。有一本書叫《代碼整潔之道》。
少用全局變數,函數圈複雜度不要過高,函數體不要過長,等等,都能使代碼易於調試和維護。
微博上看到的,原po是@虐貓狂人薛定諤,你們隨意感受一下( ̄▽ ̄)ノ
對稱,比如send對recv,read對write,init對fini,甚至參數形式都是一致的。
一個典型的處理流程:
init();
i=recv(buf, len);……i=send(buf,len);fini();因此我覺得new和delete就不夠美……不夠對稱,如下:
foo=new Bar;
………delete foo;不能忍UC雙11那天的歡迎界面 來自 @尹曉
感受下 我寫的
https://github.com/ccsdu2004/gaudio/blob/master/echo/gecho.cpp
雖然不是代碼出生,但是寫到一種境界的時候,代碼也變成一種圖形了,美不勝收。。
找公司里最亂的代碼(一定有的),然後根據《重構》,《代碼整潔之道》,可能的話加上設計模式,重構一遍,差不多就能感受到了
有的,當你遇到過翔一樣的代碼時,你就會覺得其他的代碼是多麼的美好優雅
適當的空白行,不要嵌套過多等等代碼大全講了很多。
推薦閱讀: