我們或許都曾經歷類似的經驗──覺得自己愈來愈熟悉一個程式語言,甚至很懂得它的特性,所以我們可以寫出其他程式員在乍看之下,不容易理解的程式碼。這樣的程式碼,不僅一方面可以達到它應該要有的作用,而且另一方面,也同時展現程式撰寫作對於語言本質的了解。因為不是對於語言特性有深入了解的人,是寫不出這樣子的程式碼。

曾經也有過一個程式設計競賽,是在比究竟誰寫出來的「Hello World!」程式是最難讀懂的。在這個競賽裡,的確可以看到參賽者各自運用他們對語言特性的了解,寫出了令人難以想像的程式碼,即使作用都很單純──就只是印出「Hello World!」。

從競賽的角度來看,這樣的活動是很有意義的,因為它考驗了參賽者對特定程式語言語法的熟悉程度,以及他們的想像力及巧思。甚至從藝術的觀點來看,這也讓程式碼作品本身成為了某種概念下的藝術品。

不過,當程式設計運用於軟體開發,在工程上的重要性,就遠大於在藝術上的重要性。如果能同時將工程及藝術發揮到極致,固然是理想狀態,但是在二者不能兼顧之下,恐怕還是得以工程上的需求為重。

從務實的層面考量
舉個例子來說,如果你對C或C++語言有一定的了解,就會明白++這個運算子套用在變數i上,可以寫成++i或i++兩種型式,而且分別具備兩種不同的意義,它們不僅代表著對i遞增1的動作是在賦值動作之前或之後不同,也影響著算式(expression)本身,究竟是個l-value或是r-value,甚至還有人會告訴你++i運算起來還會比i++快。光是一個小小的語法特點,其中就充滿著好些學問,更別提寫成程式之後,可能會有的諸般變化。

當然,只要你對C/C++語言有足夠的了解,你一定能夠將這兩種寫法的效應區分出來,但是能夠練出基於直覺就區分出這二者的作用,恐怕就不是那麼容易,在看到程式碼之後,可能還是要在腦中思考、推演一下,才能夠確定最終會得到的結果。

我們不免要回頭來想,究竟是賣弄程式語言的「炫技」來得要緊,還是老老實實、不嫌累贅地,多打幾個字、多寫個幾行,即使程式碼看起來沒有那麼的酷呢?

老實說,這真的跟個人風格甚至跟程式語言社群的文化息息相關。就像之前曾經介紹過孟岩在名為《Ruby 1.9不會殺死Python》的文章中,所提出的「魔幻語言」及「簡約語言」的分別一樣。

在這篇文章裡,他有很好的觀察及歸納,他說:「『魔幻語言』擁有豐富的特性,聰明的技巧和意想不到的奇效,永遠有發掘不完的奇技淫巧,總能找到讓人匪夷所思的yet another way」,像C++、Perl、JavaScript和Ruby就是屬於這一類。而在光譜的另一邊裡頭,「『簡約語言』崇尚清晰直接,夠用就行,要求代碼容易理解,寧可笨一點、累一點、多寫一點代碼,反對出人意料的技巧,反對故弄玄虛」,例如C、PHP、Python和Lua就是屬於這一派的語言。

語言本身語法設計,以及傳統、文化,還有社群的風氣,都會影響到寫出來的程式碼究竟是「簡約」或是「魔幻」。當你學習或參考的範例程式碼,滿滿的都是「魔幻風」時,設計者很容易就會被感染走上相同的路線。當然,這不代表魔幻語言寫不出簡約風,而簡約語言也有可能表現出相當魔幻的程式碼。

簡約的好處

簡約或魔幻究竟孰好孰壞,很難論定,也各有其擁護者。文化或個人風格畢竟深植在一個人的想法之中,並不那麼容易改變。

大抵事物都是如此,沒有任何一邊有壓倒性的獲勝,通常就意謂著各有長處及短處。不過,就我個人來說,是比較傾向簡約一派的,原因為何,在此文中略做分享。

事實上,我認為孟岩所言的「簡約」亦可說是「樸拙」。「拙」這個字有點笨的意思。相較於「魔幻」表述方式的精巧,「樸拙」的方式就顯得有點笨拙。怎麼說呢?好比以下的寫法(C/C++):

if( i=k++ )
{
// 做些事情
}

它的意思是把k的值賦予i之後,接著將k的值加一,並且在條件式中判斷i此時的值是否不為零,倘若其值不為零時,便執行條件式中的述句。相較於以下的寫法:

i = k;
k = k + 1;
if( i != 0 )
{
// 做些事情
}

前者顯得簡潔許多,而且充份表現出程式設計者對於這個語言特性的了解。那麼,既然如此,有什麼原因要採用後者,而不使用前者的寫法呢?

我相信對於大多數的人來說,後面的寫法其實是比較直覺的,因為它也不試著用一些拐彎取巧、或是展現炫技的方式,只是很單純透過平鋪直述的方式將目的表達出來。即使程度稍差的入門者,也能輕易理解,而且不存在太多誤解的空間。

而在這個例子中,前者的寫法還有個容易犯錯的地方。試想,倘若程式設計者的原意是i==k++,而非i=k++時,則這個很容易發生打字錯誤的情況(少打了一個=),就會導致結果截然不同,因為條件述句中的動作,有可能會從應該要執行而變成了不會執行,或是從不會執行變成了會執行。

從這個例子裡,你可以明白到,這種「樸拙」的方式的優點,起碼有兩個。首先,它容易理解,而且不容易誤解,此外,它讓程式撰寫者不容易犯錯。從這兩個優點來看,可以說是完全著眼在實際開發時的生產力而來的。正如過去時常提起的一個論點:軟體實際開發的生產力,並不只是將撰寫程式的時間計算進來,還必須除錯、或是日後的擴充及維護。上例中的錯誤只要不小心踩到,或許就會花上你不少時間,才能找到問題。

因此,「樸拙」的方式是寧可在撰寫時,用看起來比較「笨拙」的方式來表達,也不希望透過取巧的方式,但卻增加可能犯錯的機會,或是影響到程式碼的可讀性。

事實上,即使原始碼看起來比較「笨」,但在編過編譯器編譯之後,不見得會比較「笨」,也不見得會比較沒有效率。

有些早期的程式語言,之所以創造出一些取巧的特殊寫法,有時候是基於效率的考量,允許設計者透過一個表示方式,來提示編譯器編譯出更有效率的程式碼。但是,時至今日,編譯器最佳化理論技術早已成熟到一個境界,使用「樸拙」的方式,並不會有太多效率上的問題。

在這個條件式的判斷例子中,正因為C/C++語言都是在條件判斷式中,以算式之值為0或非0值來做為條件是否成立的基準,才會衍生出之後若干的種種問題。

當然,這寫法無疑是夢幻的,因為它創造出更多的可能。但是著眼在可讀性,以及更重要的——不容易犯錯的前提下。

在語法上,同家族的近親Java就將這個語言特性給取消掉。在Java裡,條件判斷算式的型別不能是整數,而限定在boolean型別。這形同廢除了許多魔幻的空間。一個新的語言將一個舊的、更有彈性、更有威力的「聰明」語法給取消掉,換成了一個「笨拙」的語法,肯定是經歷過一番反省。我想,這背後的中心思想就是「大巧若拙」。

作者簡介


熱門新聞

Advertisement