就算是老手,面對複雜的運作原理,也常有不知從何開始之感,此時,若有個臨摹對象作為參考,我們對於對相關演算法的理解或實現,就能有個大致的方向。

事實上,程式臨摹並不是新手才需要做的事情,老手也能透過特意的臨摹,獲得各種演算法的實現機會。

依樣畫葫蘆

談到「臨摹」,多半會想到的是對書法或繪畫的模仿,學習者在自身能力基礎上,儘可能地實現出接近原作的作品,而在這個實現過程中,我們期待對於原作的整體結構、運用之技巧,甚至創作時的理念或意境,能有更多的認識或體會。

在程式設計領域,其實也有臨摹的概念。面對語言、程式庫、框架等技術,開發者總會希望有範例程式碼,因為期待能藉由依樣敲打程式碼、組合元件的過程,認識如何以該門技術表達想法、體會這門技術適用的情境,以及熟悉發生錯誤時的應對方法等。

希望有範例的目的,簡單來說,雖是依樣畫葫蘆,然而,臨、摹兩字其實各有意涵。

「臨」是按照原作去寫或畫;「摹」是用紙或絹蒙在原作之上寫或畫。從這點來看,程式設計新手看著文件一字不漏地敲打、設定,就像是「摹」的動作,這時主要是在熟悉工具的特性,對語言、框架等來說,就忙著在熟悉語法、組態方式等,目的都是著重在認識工具怎麼使用是對的,而何時的用法又是錯的。

熟悉工具特性的過程中,多少都會認識到範例程式想解決的需求,以及解決問題時的思路;在同樣或類似的需求情境下,試著以相同想法來衍生,自行敲打、設定、建立起可解決問題的程式,是屬於「臨」的過程,白話來說,就像是把書闔起來,測試自身可否寫出程式的過程。

對開發者而言,臨、摹的過程會不斷的迭代進行,能讓我們在各種範例程式中,不斷地解決需求、熟悉工具。

透過重構來臨摹

開發者應該都認同「知道原理是一回事,想要實現又是另一回事。」這句話,例如,在K了一堆論文,認識演算法的原理之後,最常浮現的念頭應該就是「有沒有程式碼啊?」若是又面對一坨浮雲般的流程圖或虛擬碼時,更會有種「阿鬼!你還是講中文好了!」的想法。

不知道如何以手邊的技術將原理實現,就像不知道如何用手邊的畫筆來勾勒出相同的作品,在尚未拆解出作品完成前的每個步驟前,就算找到實現原理的程式碼,也可能因為程式碼的規模過於龐大,或者是流程交織過於複雜,此時若想要臨摹,往往也無從下手。

面對這些已實現了原理的程式碼,開發者可以試著透過重構來找到起點。

舉例來說,演算法在談論運作的原理時,應該都會區分為幾個步驟來說明,如果能夠大致找到這些步驟的對應程式碼,試著將之重構為獨立的區塊,或者是抽出為函式,這麼一來,原有程式碼的主流程,就會越來越清晰,越來越接近演算法原理中列出的步驟。

例如,想要處理A、B兩個凸多邊形交集,原理是保留A在B中的點、B在A中的點,以及A、B邊的相交點,然後將這些點以逆時針排序。雖說原理看來簡單,然而,實現的程式碼,可能就包含了線交點、點是否線上、點是否在多邊型內部、逆時針排序等內容,而且,每個部份的程式碼實現,都有一定的份量。

我們可以試著先辨識出程式碼中,與原理步驟大致符合的部份進行重構,接著,才針對這些重構後的部份,再重構出排序等部份,必要時,可持續細部重構下去。這就如同拆解繪畫作品的過程,在過程中,我們也可以進一步留意到,透過程式設計實現演算法時必須留意的技術細節。

不過,透過重構實際的程式碼來臨摹,對初學者可能難度稍高,此時,可以試著從談重構與模式的文件中臨摹,而這類文件中多半會有重構前的程式碼,以及重構後符合某種模式的程式碼之對照,基本上,如果採行此類方式,我們可以同時認識到重構前程式碼的問題,以及重構後模式之作用,也是一種臨摹之法。

然而,這種臨摹的重點在於重構的過程,請記得在不看文件的情況下,試著重現整個重構的過程;另外,不建議依樣畫葫蘆地套用模式,如果沒有既有程式碼,就依樣畫葫蘆地試圖套用某種模式,這麼做並不是種臨摹練習。

透過「翻譯」來臨摹

如果開發者已經熟悉一門語言,並試著去熟悉另一門語言,將同一個需求分別以這兩門語言來實現,會是一種不錯的練習。除了語法層面的不同之外,能夠發覺風格、典範上的不同,是這個過程中最有趣的部份。

以這種方式來臨摹時,重點可特意放在模仿新語言的文化,這就像是在仿造不同畫派的畫風,有時對新畫風的特意模仿所帶來的體驗,可能有助於過去既有畫風的突破,而在新語言中獲得的啟發,也會加深我們對既有已熟悉語言的認識,甚至影響程式撰寫的風格。

有時候,是另一種情況,例如,開發者想用某個相對冷門的語言來實現某演算法,然而難以找到該語言的實現範例來參考。幸而,現在有各種語言的開放原始碼專案,我們可以找個已熟悉語言實現之專案,透過將該語言翻譯為開發者目標中的語言,而這也是個可行的臨摹之法。

作為一門冷門語言OpenSCAD的使用者,這大概已經是我最熟悉的做法了,其難處在於,就如同翻譯這門工作本身,並不單純只是符號的轉換,而是在重新詮釋原作,若要透過「翻譯」來臨摹,必須對原作有一定的認識,這時,重構臨摹的手法經常會派上用場,接著,我們還必須找出目標語言中適合的作法,才能做好這份任務。

透過翻譯來臨摹的效益,的確是雙向的,因為,我們在既有熟悉的語言部份,往往可以透過翻譯臨摹的過程,得到更深入的認識,並且獲得更多的技巧;而在翻譯的目標語言中,我們也可以獲得各種演算法的實現機會,若能順利在目標語言中實現,此時所獲得的能力,又可以回饋至既有熟悉的語言,在使用臨摹對象的程式庫時,也就更能掌握其特性了。

臨摹不只是新手的事

在軟體開放原始碼盛行、各廠商方案眾多的這個年代,我們親手去實現演算法的機會並不多,或許也沒必要重新打造輪子,畢竟,還有更多要忙的事情,然而,正如〈Kata 心態〉中談到的:「雖然我們常說:不要重複造輪子,但這並不代表,我們連造輪子的能力都不去操練。」

如果開發者需要操練造輪子的能力,又不知從何開始,特意地透過臨摹來進行,或許是個不錯的方式,特別是透過翻譯來臨摹的方式。

雖然對於眾多開發者而言,可能沒機會像我因為玩冷門的OpenSCAD,而必須自行構築出各種基礎元件,然而,若你打算特意地藉由臨摹來從頭開始構築某個方案,也可以從中獲得必要的練習。

專欄作者

熱門新聞

Advertisement