Saturday, November 12, 2011

程式設計的兩個觀點 (1/2)

程式設計的兩個觀點 (1/2)
程式語言PASCAL之父Niklaus Wirth曾經說過 ”Algorithms + Data Structures = Programs”。倘若程式是由演算法及資料結構所組成的,那麼設計程式的活動,似乎也應當是由演算法及資料結構的設計及運用所支配著。
許多程式員們都曾修習過「資料結構」以及「演算法」的課程。但說實話,肯定有一定比例的程式員在離開校園投入職場後,往往會發現在自己實務的開發經驗中,在這兩門課程中所學的,「似乎」都很少發揮作用。我相信,對很多程式員來說,心中難免存著疑惑,許多人都說這兩件事情對程式設計來說十分的關鍵,那為什麼在自己實務的開發經驗中,很少感受到到它們的重要性呢?可是,會不會也時常聽到別人說,倘若你不懂資料結構、甚至是演算法,那麼你不過只是一個撰寫程式碼的工人,而無法成為一個有價值的程式員?對此,你是否曾經感覺到憂心或是迷惑呢?
事實上,隨著電腦科學的發展,許多有用的資料結構及演算法,早已發展到十分成熟的境界。同時,這些資料結構以及演算法,也已經被廣泛的實作並且以各種對程式員來說的高階形式包裝。在學校修讀資料結構及演算法課程時,教科書也許花上相當多的篇幅討論排序演算法,你或許會學到Bubble SortInsertion SortMerge SortQuick Sort等等排序的演算法。但當你投入真正的程式設計工作時,你會發現教科書上所介紹的演算法都有現成的程式庫提供實作,例如C語言的標準函式庫中的qsort()函式即為提供Quick Sort演算法的函式。對開發工作來說,需要另行設計出自有的排序演算法,其機會實在是微乎其微。
談到了排序,讓我們來看看一個值得一提的例子吧。Java標準程式庫中,有個叫做java.util.Arrays的類別,提供了一系列重載版本的sort()函式,允許你對各式型別的物件陣列進行排序,甚至允許你自訂物件在排序時的比較方式,是個相當通用而且威力十足的排序函式。
你也可以和我一樣翻開手邊的JDK原始碼。我手邊這個類別的.java檔原始碼版本是1.59,掛名作者的,正是2006年造訪台灣、現正任職Google的兩位大師-Josh Bloch以及Neal Gafter。如果你找出sort()函式的說明,原始碼中明白的指出,其排序演算法乃是採用一篇標題為 ”Engineering a Sort Function” 的論文中所提出、經調適後更為快速的Quick Sort演算法。如果你更進一步檢視,你會發現當欲排序的元素個數小於7時,它便不再使用Quick Sort,而是採用Insertion Sort
即使是像這兩位卓越的大師,在他們實作排序的功能時,並沒有自行設計出新的演算法,而是採用前人所發展的演算法。這並不損這兩位大師的價值。這個演算法並不是出自於一般教科書中所介紹的演算法,而是出自於學術期刊上所發表的論文���即使你熟讀教科書中的Quick Sort,也不見得能自行發展出如此快速的Quick Sort。而且,在Java標準程式庫中的這個實作中,更是兼顧了實務的考量,因為在元素個數很小時,時間複雜度為n^2Insertion Sort,實際執行速度還勝過號稱平均會有n*log(n)Quick Sort
對現在的程式員來說,演算法的影子在設計工作中之所以愈來愈淡,有一個核心的原因,便是在於我們所需求的絕大多數各種演算法,早已在層層的分工下被發展的十分完備。有人發展原始的演算法、有人持續的進一步改進、有人找到實務上調校的技巧、有人以高階的形式加以包裝,最終成了可供客戶端程式員直接運用的產物。於是,到了程式員手上,只消呼叫Arrays.sort(),排序問題就被神燈中冒出來的巨人神奇般的解決了。
排序演算法在此僅僅只是一個例子,但不可否認的,這數十年整個電腦科學學術界的研究成果,早已將能滿足我們需求的各種演算法和資料結構,以相當高階的形式包裝起來,對程式員來說唾手可得。這使得程式員即使完全不懂得這些演算法及資料結構的細部運作,仍然有可能如魚得水的加以操控。事實上,不僅僅是演算法領域如此,整個電腦科學領域的研究成果,皆在此類層層的分工下,為程式員提供既高階且便利的工具。
所以,你或許會留意到,現今的程式員似乎更為重視在演算法及資料結構以外的東西。例如,程式員們更為重視架構上的「設計」。這設計並非演算法或資料結構的規劃及設計,而是規劃、安置軟體建構區塊之間的連結及關係。從演算法、資料結構出發的程式設計觀點,其眼光放在如何讓計算機器以有高執行效率、節省資源的方式來解決特定的問題。但從架構觀點出發的程式設計觀點,卻是著重在如何以有彈性、容易擴充、容易改變的方式,迅速的組裝搭配各種建構區塊,並從中獲取最大的生產力。
這兩種程式設計的觀點,呈現出截然不同的面貌,也時常的惹來喋喋不休的爭論。從演算觀點來看程式設計的人,會認為不懂演算法、或不知如何設計演算法的程式員,設計不出高效、節省資源的程式,所以自然缺乏價值。著重架構觀點的人,也同樣的認為許多擅長解題的人、不擅長設計出架構上具有優勢的程式碼。
這中間的認知衝突,乃是源自於程式設計本身多面向的特質,程式設計既有演算的面向,也同樣的具有架構的面向。程式設計也因這多面向的特質,造成了盲人摸象的情況。站在演算觀點的人說程式設計就是演算法加上資料結構,而立足於架構觀點則說,程式設計與建造建築物差可比擬。對同樣的一項活動,竟然會有差別這麼大的描述方式。
早期對程式設計的活動,偏重在解決特定的問題。這是因為早期對電腦程式的需求,皆起因於想要解決特定的演算問題。設計者可能希望計算給定一組城市以及城市間的距離,計算出任兩個城市之間的最短距離,於是我們有了最短路徑的問題。隨著各式演算問題被探索、研究。這些資產逐漸的被累積,便成了現今程式員得以垂手便取的現成產物。這些產物不僅現成,而且還不容易被超越。
當程式員愈來愈不需要自行開發、實作解決各式特定問題的程式碼時,程式員手上有的,便是許許多多的建構組件,這些組件的來源,正如前段文字所述,乃是以多年的研究探討為基礎,再加上軟體產業的層層分工而來的。能解決問題的組件多半毋需重新再造輪子,況且自己再造的輪子其品質也難以和現成輪子匹敵,那麼很自然的,程式員便會將重心逐漸轉移到如何更有生產力的裝配這些組件。這些組件對程式員來說,都像是完全不透明的黑盒子一樣,程式員毋需理會其內部實作,只需要從抽象高階的觀點來理解這些組件,並妥善的加以運用即可。一時之間,看似架構觀點所重視者,其重要性又凌駕於演算觀點所重視者。
Reference:

No comments: