多讀別人寫的程式???
多看別人寫的程式
曾經聽別人說:
寫程式沒有什麼祕訣,
就是要 "多看別人寫的程式"。
這句話一眼看上去似乎沒有什麼可以爭議的,
反正那麼多的語法,
那麼多可能的用法,
我不看看別人是怎麼應用它去寫程式,
難道要我自創用法嗎?
話是這麼說的沒有錯,
但是
-
別人寫的程是你看得懂嗎?
-
同樣功能的程式,
寫法一定有五至十種,
看來看去每一種都不錯,
到底要用哪一種呢?
-
看一看,
看得懂或是看不懂不重要,
反正把它背下來不就好了嗎?
其實 "多看別人寫的程式" 這句話是有語病的,
沒有好好的分析它的話,
甚至會嚴重地誤導你的學習,
所謂的 "多看別人寫的程式" 這種說法和我們在學習外國語言時的座右銘
"多看、多聽、多說" 是一致的,
但是很重要的是你要在適當的環境裡來實行才有效果,
例如學美式英文,
你就是要到美國去,
或者至少你接觸到的人必須是土生土長的美國人才可以,
這樣子你多聽的才是正確的,
多講的才是正確的,
如果你在一堆中國人當中講練習英文,
那就不必了,
不會道地的。
那麼講到寫程式呢?
我們在很多地方都可以看到很多人寫的程式,
CPU 都可以執行,
答案都是我們想要的,
這樣子的程式應該可以看看了吧?
錯!
CPU 可以執行、
且不會出錯、
會跑出正確答案的程式不見得是你應該學習的程式。
讓我們這樣子來解釋:
CPU 是一個很簡單的東西,
它能做的事很單純,
所以操作命令它的程式當然也會很單純,
你一定很容易可以看得懂的,
所以簡單的說一個好的程式應該是很單純的,
很淺顯易懂的,
邏輯清楚不混淆的 ...,
(請注意:很單純淺顯的程式不一定很短喔!)
你該看該模仿的程式就是這樣子的程式。
如果一個程式讓你乍看起來覺得前後的敘述糾葛在一起,
前後的資料變數看似相關又好像無關,
迴圈三四層,
條件測試三四層,
全域變數一堆,
每一個函式二三百列敘述,
變數都是用 x1, x2, x379, y10ab, yvs, srw, ...
這種沒有意義的名字,
那就建議你別費神去看了,
這種程式也許可以執行,
有錯誤的時候根本沒人敢去改它,
它像是一張多維度的網子,
每一個敘述雖然只使用三五個變數,
但這三五個變數在一二十個敘述中使用到,
這些敘述又使用到其它的變數,
如此幾乎所有的變數和敘述都相關了,
牽一髮而動全身,
誰敢去動它?
當然也就別提去修改增加它的功能了。
多看別人寫的好程式
在回到 "多看別人寫的程式",
現在應該改為 "多看別人寫的好程式" 了,
好的程式是邏輯清楚、
淺顯易懂的,
那麼其實多看程式要你學習的是如何將抽象複雜的實際世界資料,
抽絲剝繭單純化、
規則化,
如何將紛亂的處理程序組織起來,
用清楚不拖泥帶水、
淺顯易懂的程式表達出來。
什麼是 "好" 的程式,
不是靠 CPU 或是編譯器來檢查的,
而是你自己需要去判斷的。
有的人做事雜亂無章法,
有的人做事有條有理,
通常我們在處理事情的時候都會將相關的事物放在一起,
無關的事物則各自歸類,
如此的話就是看起來有條有理的那種人,
如果完全不做整理,
所有的事情都混雜在一起處理的話,
就是屬於雜亂無章的,
我不是在批評做事雜亂無章的人,
其實這類的人如果能把事情一一完成的話,
通常是記憶力比較好的那種人,
(如果亂成一堆又無法完成,
我們就不必討論了。)
不過這種人終究是少數,
通常一般人記憶力沒那麼好的話,
就需要靠整理的功夫,
分門別類,
就可以減少出錯的機會,
CPU+Memory 這個組合因為記憶力超強,
因此它可以做的事情包括很多亂無章法的事,
但是千萬別學它。
先想一想自己該怎麼做
再回到 "多看別人寫的好程式" 這句還是有一點點問題的話上面,
你以一個學習設計程式的角度來看,
絕對不是看一看、
背一背就了事的了,
要分析原來的程式要求,
先想一想看自己會怎樣著手,
資料如何整理,
變數如何安排,
程序如何設計,
如何利用 C 程式語言來表達你要處理的資料和程序,
然後再去比對別人的程式和自己的程式有什麼差別,
(其實你應該要還原別人的程式,
看到了別人的資料表達與組織方式,
程序的邏輯安排,
再和自己的資料與程序做一個比較),
學習程式設計不是學習花俏的語法,
而是學習如何將實際世界的問題抽象化、組織化,
成為資料與程序的世界。
看別人的程式的時候應該要
-
判斷它的好壞,
-
分析原始題目中資料組織與處理邏輯,
-
分析程式用的資料組織與處理邏輯,
比較其效率,
-
更改題目的要求,
試試看你能不能直接更改別人的程式來完成要求,
好的程式邏輯清晰,
應該是你可以更動的,
-
整理你的收穫。
注意:
-
前面我們提到同一種程序邏輯,
用不同的語法來表示的話,
可以看到很多不同的風貌,
到底要用哪一種呢?
應該要用最容易看得懂的,
除非有非常嚴格的效率要求,
才另外考慮。
-
迴圈好多層的話,
應該考慮把內層迴圈用函式包裝起來,
例如:
int i, j;
for (i=0; i<52; i++)
for (j=0; j<7; j++)
calculateEverydayEarning();
可以考慮寫成
int i, j;
for (i=0; i<52; i++)
calculateEveryweekEarning();
...
void calculateEveryweekEarning()
{
int i;
for (i=0; i<7; i++)
calculateEverydayEarning();
}
在看後者時,
我們看到個別都是一層的迴圈,
結構相當簡單,
你也很容易可以假設如果
calculateEveryweekEarning() 正確的話,
這上層迴圈做 52 次幾乎不可能出錯,
然後再去看內層迴圈的動作,
這樣子的兩部份程式的複雜度比起一個兩層迴圈的複雜度來說應該是低得多了。
-
以前在運用 FORTRAN 或是 BASIC 語言撰寫工程方面問題的時候,
常常會看到一個函式對應的流程圖可以用十來張紙才畫得出來,
其中流程錯縱複雜,
就像是一團弄亂的毛線球,
其實現在在很多非資訊的應用領域裡,
程式還是一樣是那樣子寫的,
這種程式絕對不是資訊系同學該寫的東西。
資料與動作流程是兩個相互影響的東西,
有時資料結構的不適當,
會導致程式流程異常的複雜,
例如:
寫一個二維矩陣相乘的程式如果你用一維的陣列來表示的話,
你的程式當然會比較複雜,
另外有時程式邏輯沒有清楚地歸納、
適當地分段區隔開來,
會導致資料的混淆與錯亂,
例如:
全域變數就好像一堆人吃大鍋飯菜,
又不分公筷母匙的,
你一筷、
我一匙的,
你怎知道飯菜到底是誰吃了,
是誰汙染了...,
相同地,
全域變數內的資料可能在程式任何一個地方被修改,
完全不配合各自區段之邏輯,
其內資料的變化就很難掌握了。