昨天同人在〈又見少了概括性論點〉提到〈必須面對的真相─五大程式設計迷思〉在文章結構上的問題。其實那篇文章除了結構問題之外,同人也在該篇文章內容中,發現了一些值得探討的問題,因此想在這篇文章發表我的看法。
以該篇文章內容而言,同人認為值得探討的有兩個地方,一個是程式語言一直再改變的迷思、另一個則是作者建議讀者,儘量避免用遞迴的方式來寫作程式。第一個問題只是沒有交待清楚作者想要表達的概念,而第二個問題就是嚴重的偏見了,值得讓人進行思辨以建立正確的觀念。
為什麼程式語言一直再改變是個迷思?那篇文章就有讀者質疑這個迷思,他說:
關於這部份,我覺得不如講"只有XXX語言才能作某件事情"
我不覺得"程式語言的改變"是迷思
不過,同人認為所謂「程式語言一直在改變」的迷思,作者的意思不單單只是說程式語言沒有一直在改變的意思,而是還有更深層的涵義沒有表達出來。事實上,程式語言一直再改變是個事實,我們不該說它們沒有一直改變。而是這些改變並不會讓我們先前對程式語言學習的投資白費。
同人認為,作者提到「電腦程式語言的演進,就像人類的語言演進一樣,有其一定的過程」是正確的。但對程式語言的學習而言,重點並不在於「程式語言不會一直在改變」,而是在於改變通常不會影響程式語言舊版本的語法,就算存在一些新舊版本不相容的影響,但只要掌握基本觀念,新的改變是不需要重新學起,而且我們經常還能在不同的程式語言之間,發現彼此相通的地方。
其實程式語言會不斷演化是必然的,「成熟」的程式語言反而是象徵衰退的開始。因為隨著時代的進步,程式設計的問題只會愈變愈複雜,造就了新的程式設計概念。如果程式語言太成熟,以致無法改變來加入新的程式設計概念,那麼它們將被時代所淘汰。所以,問題不在於程式語言是不是臻於成熟,因為改變是必然的,重點在於你投資在程式語言的學習並不會白費。
再來,作者提到「千萬不要認為電腦已經跑得很快了,就不注意演算方法的選擇。」同人認為是正確的。但他舉例指出遞迴函式效率較不佳、比非遞迴寫法的程式容易做重複計算,建議大家儘量避免用非遞迴的寫法來進行程式寫作的說法,同人就完全不能夠認同作者的看法。
沒有錯,遞迴函式的確比非遞迴的寫法會佔去更多的記憶體,但以空間換取時間的角度來看,說遞迴函式比非遞迴寫法更沒有效率,則是過份簡化的說法。即使以作者舉計算費式數列的遞迴函式的例子來看,遞迴函數可能有重複計算的問題,但到底費式數列的 n 值要多大,遞迴函數的執行速度才會發生顯著的影響,這卻是值得探討的問題。
如果在一般解決領域問題的條件限制上,都不見某種演算法的缺點,會產生顯著的影響,但卻能享受到該種演算法的優點,那麼我們是不是應該選用這樣的演算法來設計程式呢?答案想必是肯定的。這不是把問題推到硬體速度夠不夠快的問題,而是演算法的選擇應該透過客觀的分析與理性的思考,才能找到最適當的選擇。
這讓人也發現到程式設計常見的迷思,很容易誤導我們對程式開發的看法;太過相信經驗法則而缺乏對問題的深入思考,容易人云亦云地以為該用什麼演算法或避免什麼演算法,而那些都是程式設計師自以為是的偏見。
我想到哈米尼斯對同人分享永續物件設計所提出的意見,他提到:
就現實問題而言,一家公司裡,可能許多套系統都是由不同的廠商開發,若以這樣的角度來看,這一個架構似乎有點問題。
再來則是效能考量的問題,我認同這一個架構下,可以讓開發和維護變得簡潔,但若是遇到某些特定的系統,如下單系統,它強調的重點會在反應時間及大量資料處理,若是以此為考量,這樣的架構似乎又繞了太多層?
哈米尼斯的意見正是同人在工作上常面臨的質疑,但實際上我所提出來的設計是經過驗證過的架構,運用服務導向的觀念、以及物件導向設計的設計手法,已成功整合銀行各種不同廠商開發的子系統。它實際應用在銀行的付款系統,所以對績效的要求是非常嚴苛的,然而實際上它的效能卻未見如哈米尼斯所顧慮到的問題。
受限於自己的經驗,很多程式設計師傾向於認定太多「系統間接層」不好,但那只是一己想法,而未經證實的猜測。如果真的擔心發生這樣的問題,應該採取行動進行驗證,而不是停留在想法的猜測,這才是程式設計者應有的積極態度。
事實上,間接層的設計只是把原來放在一起的程式碼區分好責任,去掉重覆性而已,我們對系統繞太多層的理解不一定是系統運作的真相。除非我們做了設計概念驗證,讓事實或數據說話,而不是經驗的推論,後者往往來自於個人徧見所致。
同人無意在這裡討論那一種程式開發典範比較好,因為那是不會有結果的爭論,只有在了解程式想要解決的問題之後,討論用什麼方式來開發程式才會有意義。
演算法的選擇也是同樣的道理,每一種演算法都有它的優缺點,我們應當針對問題的目標與限制來「思考」最適當的演算法,而非慣性地「記憶」那一種方法用在每一種情況都比較好、或比較不好。因此作者建議儘量避免用非遞迴的寫法,進行程式的開發,同人對此實在是感到不能認同。
更何況透過思考,遞迴函式重覆計算的問題並不難解決。例如我們可以在遞迴函數中,將已經計算過的數列項次值儲存下來,當過程中發現某個項次已經計算過了,我們可直接取值而避免再計算一次,這樣就可以增進遞迴函式的執行效能。而運用程式設計者的智慧,遞迴耗費大量記憶空間的限制,也並非不能克服。
同人一位朋友就分享他在繪圖程式設計的經驗,他曾改進過填色的遞迴函式。原來一個點向四面擴散填滿顏色要用到大量的遞迴空間,但只要改變以線或區域為單位向四面擴散,就能大幅減少填色所需之遞迴空間,克服遞迴函式的先天限制。
所以問題並不是遞迴可不可以用,而是程式設計者懂不懂得用思考來創新設計技巧;不懂得在解決問題上尋求挑戰與突破,那才是讓程式設計能力停滯不前的最可怕迷思呀。
剛剛也剛好看完那些所謂的迷思
原本想說些什麼、剛好就看到有人認真評論了一番。
與小弟想法不謀而合。希望那些看了那篇文章的人有機會看到這篇逆向省思一番、尤其是提到遞迴的部份尤其是以偏概全的成份很大。
我想他提出的論點有點太淺薄了
不管是演算法還是系統架構
在透過複雜度分析後就可以明確的知道效能的好壞
真正插進的架構會表露無遺
但開發效率跟執行效率勢必是互相矛盾的
只能在這兩者中取一個平衡點