# A0002 見山是山,見山不是山,見山只是山 ## 寫程式如禪學 **「見山是山,見山不是山,見山只是山」**[^1],這是禪學領域知名的人生哲理,描述著人生會經歷的三個階段。其意思是,在人生年輕歷練尚淺時,看到山就是山,不會有太多的想法;當進入中年,有一定人生歷練的時候,看到山總是會有些不一樣的體悟出來,或許覺得壯闊、或許覺得莊嚴、或許覺得寧靜,端看山所帶給自己的感覺,而不是山本身;最後到了經歷更多的老年,在看透了許多事情後,看到山反而沒有之前的包袱,認為山就是山,沒有必要過度解讀。 [^1]: 《指月錄》卷二十八。青原惟信禪師曾對門人說:『老僧三十年前未參禪時,見山是山,見水是水。及至後來,親見知識,有個入處,見山不是山,見水不是水。而今得個休歇處,依前見山只是山,見水只是水。』。 這樣的人生三境界,在寫程式的領域也是如此。 ### 境界一:見山是山 初次接觸寫程式的時候,通常都是從語法開始學起,所以看到語法就是語法,也就是**見山是山**。比方說,當學到了 if、for、class、function 等等的語法,心中所想的是,需要判斷式就加入 if、需要批次處理就加入 for、需要將資料包起來就加入 class、需要打包一段程式碼就加入 function。**見山是山**,語法就是語法,任何語法使用起來都是直觀的。 ### 境界二:見山不是山 隨著寫程式的時間越來越長,不論是自己經驗的心得、還是前人提供的指導、或者書本撰寫的知識,對於語法的使用概念會越來越不單純。可能會有以下情境: * 得知「使用switch 比 if 有效率」,就開始覺得要把任何的 if 都替換成 switch。 * 得知「用泛型做 for 會更有通用性」,就開始覺得 for 都要寫成泛型。 * 得知「相關的觀念就一定要封裝」,就開始覺得不寫 class 罪大惡極。 * 得知「不包 function 難以複用程式碼、可讀性低」,就開始覺得包 function 才是正確的寫法。 自此語法不再是語法,都無形地被延伸概念所取代,**見山不是山**。從上述情境可以發現,這境界的程式設計人員有很重的包袱,可能是擔心違背了哪個程式觀念、也可能是對自己沒信心,導致在看到不錯的程式寫法或觀念之後,就拼命套用、彷彿非要用上一堆方法才是寫程式的正道。當寫程式就是套方法、套公式的話,產出的程式碼通常不直觀。由於是套用公式,通常從某些來角度看,水準會還不錯,可惜寫起來稍嫌不夠精準[^2];運氣不好的話,可能用得不恰當,反而把程式碼變得難以維護。 [^2]: 精準的程式碼,指的是易讀、易懂、易 trace,不殺雞用牛刀,不囉嗦地完成簡單程式碼。 ### 境界三:見山只是山 在經歷過許多的程式觀念、方法、公式的轟炸後,漸漸地回歸語法與觀念的本質,也就是**定義**。任何寫法都是在清楚定義、明白優缺點的前提下完成。心中所想的是,需要判斷式就加入 if、需要批量處理就加入 for、需要將資料包起來就加入 class、需要打包一段程式碼就加入 function。文字描述看起來與**見山是山**的境界沒有差別,但實際上要如何判斷「需要」卻是非常不同的。以 class 與 function 的例子來說,**見山只是山**的境界會很清楚要完成目標去包裝 function 或 class 的利與弊,判斷的準則都是從定義做起,然後**必要就去做、非必要就不去做**,而不是**見山是山**的**單純想到就去做**。 **見山只是山**的程式設計人員,對於語法與觀念的掌握度很高,所以處理問題的方法與工具會有很多,也因此總能產生很靈活的應對解決方案;各種精妙的程式碼,通常也是在這個境界才能寫出來的。這應該是最能享受寫程式的境界,不但思考的過程愉悅,完成程式後也頗具成就感。 ## Pointer 只是 Pointer 如果還是覺得這三個境界很抽象,沒關係,下面透過 C/C++ 的重要觀念, **Pointer** 來做說明。相信透過這個經典例子,可以更清楚寫程式的三個境界是什麼概念。 ### 境界一:見 Pointer 是 Pointer 剛接觸 Pointer 的時候,於定義上會學到「Pointer 的用途是儲存位址」;因此當書本或課堂範例提到位址,就會與 Pointer 做連結。這樣的連結,通常像是在背課文一樣,看到 ```int* p = &a;``` 就翻譯成 ```p``` 是 ```a``` 的位址。沒有什麼雜念,沒有什麼額外的想法,卻不知道應該在什麼地方使用。唯一可能使用的情境,就是當有些地方程式碼行為不如預期,像是想改變什麼值卻無效、或改變值卻造成 memory corruption,才會想到要調整 Pointer 的寫法試試。一個星號不對就用兩個,兩個星號還是不對,就再改寫法以避開 Pointer。由於很清楚 Pointer 的定義是儲存位址,所以**見 Pointer 是 Pointer**,只是可能不清楚怎麼使用,所以能免則免。 ### 境界二:見 Pointer 不是 Pointer 可是在 C/C++ 的領域要完全避開 Pointer 是非常困難的,很多情境都會需要用到。在不知道什麼時候該用,什麼時候不該用的狀況下,最常見的使用方式就是**套公式**。就像學生寫數學題目,很多時候不求甚解,只要題型與對應公式背好,考試就套公式,反正能拿高分就可以了。 下面舉幾個常見的 Pointer 使用公式:_Swap 交換兩個數值_、_Array 傳遞_、_額外的返回值_。當然,會使用到 Pointer 的公式不止於此,這裡提出 Pointer 初學最常見的案例來做說明。 #### Swap 交換兩個數值 ``` c++ void swap(int* a, int* b) { const int tmp = *a; *a = *b; *b = tmp; } ``` #### Array 傳遞 ``` c++ int sum(const int* p, unsigned int size) { int sum = 0; for(unsigned int i=0; i<size; ++i) { sum += p[i]; } return sum; } ``` #### 額外的返回值 ``` c++ void complex_mul(int a_re, int a_im, int b_re, int b_im, int* c_re, int* c_im) { *c_re = a_re * b_re - a_im * b_im; *c_im = a_re * b_im + a_im * b_re; } ``` 如果有寫過與 Pointer 相關的程式碼,肯定對這些公式不陌生,或多或少會用過這些公式。在**見 Pointer 不是 Pointer** 的境界,其實對 Pointer 如何使用還是不清楚的,但在遇到這些公式的情境,就會很自然地將對應的程式碼寫出來讓程式行為符合預期。就因為該怎麼使用 Pointer 還不是很熟,所以在學習過的公式之外,依然是不碰 Pointer 的。 ### 境界三:見 Pointer 只是 Pointer 進入到這個境界,使用 Pointer 並不拘泥於公式,僅針對定義「Pointer 的用途是儲存位址」就能發揮 Pointer 的各種效用了。或是用於通用介面、或是用於抽換 function、或是用於實現多型(Polymorphism)、或是用於隱藏實作等等。效用之多,不是隨意舉幾個例子就能概括的;各種靈活運用已經無法用套公式來辦到,而是在清楚定義的前提下,去建立自己想要的實作方案。一旦拋棄了公式的成見,就能**見 Pointer 只是 Pointer**,當用則用,不當用則不用。 ## 終能見山只是山? 如前面所說,當到達**見山只是山**的境界,不但寫程式的思考過程愉悅,能寫出精妙的程式碼,而且完成後的成就感極高。那是不是未達此境界的人,只要程式繼續寫下去、繼續練習就能達成呢?**答案是否定的**。人生可惜的就是不一定能**見山只是山**,可能終其一生都還是**見山不是山**,見到山都還是有想法、有感慨、有包袱,看不見山的本質。寫程式也是如此,許多人寫程式陷在套公式的泥沼裡,實作方案永遠都在找相似題與公式,沒有脫離套公式的時候。 為什麼會停滯在**見山不是山**的境界呢?主要是套公式之餘,沒有去思考公式的組成原理、也沒有去理解各個原理的定義、更沒有在套公式之餘問自己是否有更好的做法。長久下來,再也不問程式觀念的定義是什麼,反正程式能動就好,自然無法**見山只是山**。 其實停留在**見山不是山**的境界也不見得是糟糕的,畢竟每個人的理想與目標不同,非得在寫程式的旅程上追求最高境界是不合理的。倘若工作不需要、或者目的不需要,即使寫程式停留在最初的**見山是山**又如何?應該要看自己的需要,再來追求應該要達到什麼境界。 ## 見山只是山 那要如何才能邁向**見山只是山**的境界呢?下面分享幾個具體訣竅: * **追本溯源**:當接觸到新的程式觀念,不要只是將範例當公式使用,而是要釐清根本定義;縱使定義可能不容易理解,也可以先記起來。將來有機會再接觸到的時候,嘗試用定義思考、解讀,就會有豁然開朗的一天。 * **他山之石**:當看到別人優秀的程式碼,如果認識作者,可以請教作者這樣設計的理由;如果不認識,可以嘗試解讀,或者思考是否有更好的寫法。只有弄清楚為什麼要這樣寫、為什麼有(或沒有)更好的寫法,才能將好的方法內化成自己的技術。 * **百尺竿頭**:當自己完成一份程式碼,不要只滿足於完成的當下,永遠都要思考有沒有更好的實作方案。如果自己知道有,盡可能抽空練習將程式碼改進;如果自己不確定有沒有,也可以請親朋好友幫忙做程式碼審查(Code Review),不論審查結果如何,都能透過討論、接收更多不同的觀點,以豐富自己的知識庫。 條條大路通羅馬,其實要讓自己往下一個境界邁進的方法有很多,並不限於上述的訣竅,這裡僅就經驗提供作參考。只要有心要讓自己進步,不要一直覺得自己已經很厲害了、不要拒絕接收更多知識、不要拒絕檢討自己、**不要成天只想著程式能動就好**,總會有達到**見山只是山**的時候!
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up