紙上程式設計


iThome 網站首載:紙上程式設計

現代人太依賴電腦,任何事都在電腦上進行,忘了拿筆時應有的思考方式,忘了面對紙時的環境隔離。有時開發者會發現,自己根本不是在思考如何解題,而是被一堆語法混亂、被API名稱給困擾,此時與其胡亂地在電腦上打字,看著不斷吐出的錯誤訊息,不如關掉電腦,封閉在紙筆的世界中,專心一致地解決問題的本質性困難。

程式設計師最怕沒有電腦?

對於程式初學者,許多先進都會告誡,以文字檔案撰寫程式,使用命令模式編譯程式等以奠定基礎,這樣才能掌握該門語言技術的細節,才不會被特定開發工具給限制,將來面對問題時也才有能力解決。先進們的話沒有錯,開發工具會協助開發者處理許多細節以增進程式產能,然而基礎不足的初學者直接使用開發工具,會因為隱藏過多細節而看不清底層運作的各種原理。

直接面對編譯器等基本工具,開發者需要注意原始碼檔名、格式、編碼等問題,得瞭解發生問題時編譯器抱怨的內容為何,要明白如何佈局原始碼與相關資源,將來使用開發工具時,對於各種便捷功能或自動化流程,就能清楚明白底層的實際運作。若直接面對編譯器這樣的簡單工具,可讓開發者瞭解語言技術的本質,而語言技術是用來描述問題的工具,那麼如果連編譯器都沒有,不!如果連電腦都沒有,那麼開發者是否可完全掌握到所面對的問題本質呢?

沒有電腦?那怎麼寫程式?《黑客列傳》描述了電腦還是龐然大物的那個年代:「操作員會依照這些特權人士想要輸入紙卡的各種指令,用這些機器在紙卡上打洞。卡片上的洞代表給電腦的各種指令...」操作員會把紙卡放到「讀取器」裡讀取那些孔洞,然後等待幾個小時或幾天才能得到結果,如果程式有誤,就得拿回紙卡進行修正,重來一次這個漫長的過程,為了避免使用電腦的這種附屬困擾,獲得使用特權的人得兼具細心、耐心,完全靠腦袋仔細分析問題,以便在紙上寫出要電腦執行的工作流程。

時至今日,程式設計發展近五、六十年,開發者可在電腦上直接撰寫程式、編譯、執行、除錯,即時互動給予程式開發者莫大產能,雖然附屬困擾大副降低但依舊存在。在用語言描述問題時,關鍵字、語法錯誤會令開發者分心,考慮使用何種API會讓焦點離開問題,為了查詢組態設定,查閱文件或開啟瀏覽器,更讓開發者進入搜尋的情境,徹底把真正要解決的問題拋在一旁...結果就是...現在沒有了電腦,恐怕許多開發者一行程式也寫不了。

軟體的本質性困難與附屬性困難

《人月神話》作者Frederick P. Brooks在《沒有銀彈:軟體工程的本質性與附屬性》中指出,軟體開發的困難分為兩類:從軟體天性沿生( inherent)而來的本質性(Essence)困難,以及伴隨(attend)產品而來的附屬性(Accidents)困難。

本質性困難在於軟體本身是由眾多交錯的概念建構而成,像是資料集、資料項目之間的關係、演算法等,若要解決軟體面對的問題,就是解決這些概念本身的複雜性(Complexity)、概念間彼此的配合性(Conformity)、概念受到人、事、時、地、物等因子影響的可變性(Changeability),以及軟體本身看不到、摸不著、難以呈現結構的不可見性(Invisibility)。

附屬性困難則在於將建構好的概念實際呈現,也就是具體在電腦上實作時發生的困難。《黑客列傳》中描述了TX-0中可用的指令只有00、01、10、11,單是要讓兩個數字相乘,得連續組合這四個指令,像是10011001100001--盯著一長串由二進位制數字組成的指令「會讓你在幾分鐘內成為語無倫次的精神病人」。丹尼斯撰寫了組譯器,可以用ADD Y等較簡單的象徵形式來寫程式;高階語言避免直接處理暫存器等低階細節;具備垃圾收集(Garbage Collection)的語言,讓開發者免於自行配置與釋放記憶體,避免許多犯錯的機會。相同軟體概念在不同時空於電腦上實現時,會產生不同的附屬性困難,也會因為技術突破消除掉重大的附屬性困難,使得開發難度下降而獲得大幅的產能甚至品質提昇。

然而無論語言技術或工具如何便利,本身必然都會帶入某些附屬性困難。框架的使用必然會有組態設定問題;使用所視即所得工具,開發者能自行控制、修改的部份必然受限。附屬性困難會造成解決問題本質性困難的阻礙,例如某開發者對於大整數運算的資料結構與演算就算瞭然於心,但要以某不熟悉的語言實作時,首先都會因關鍵字、語法或API等問題而暫時絆住了腳。

紙上作業避免附屬性困難的干擾

有些公司在應徵新人時,會讓新人當場寫程式,有的是用電腦寫,有的是用紙筆寫,對於後者,多數應徵者反應通常不佳。《程式之美》作者談到應徵者典型的回應模式是:「我不習慣在紙上寫程式,平時都在電腦上寫...」作者談到其實可藉此觀察到「應徵者面對這個陌生問題時,如何開始分析」。

面對陌生問題時如何開始分析,就是要觀察應徵者如何面對問題的本質性困難。如果真正想解決的是問題本身,想要分離問題的本質性困難與附屬性困難,最好的方法也許是遠離電腦。開發者或許都有這種經驗,在程式中胡亂地調整數據或語法,像是發現迴圈中判斷停止有問題時,隨意地將>改為>=,結果「好像」對了就好...那麼你真的瞭解問題並解決了嗎?是否清楚明白迴圈中止的條件與邊界呢?

常聽及不少先進談到,由於早期電腦不普及,對於程式設計的狂熱,使得他們常在沒有電腦的情況下,於紙上進行程式設計,這使得他們不會被附屬性困難干擾,專心面對問題的本質性困難。實際上在紙上實作是一種相當重要的訓練,曾看過有公司的應徵條件之一是「能用筆在紙上寫程式,用眼睛幫別人程式除蟲(Debug)」。

在紙上寫程式既然是要避免附屬性困擾的干擾,找出解決問題時的真正邏輯,首先就得避免從語言的角度開始,改由分析問題的組成與結構出發。例如要解決的問題集合為何?需要什麼樣的流程?要產生何種結果?在紙上先將整個或部分程式概略計畫出來,接下來運用語言描述出來,這邊的語言不一定是指程式語言,也不是要求寫出語法正確無誤的程式碼,開發者可用任何能在紙上運用自如的語言,必要時亦可使用母語表示,也無需盤算呼叫特定API。例如,如果想清空項目清單,並非分心去查閱List是否有clear方法可呼叫,儘管寫下「清空清單」就好。

重點在定義問題

《Coders at work》中Guy Steele談到:「在做演算法設計的時候,使用多種語言的結合,我感到運用自如。在我需要和自己交流的時候,我總是走到白板前,寫下Java、Fortran還有混雜著APL的片段。」

實際上,這是一種真正在定義問題本質的過程,開發者若對問題本身沒有足夠的瞭解,打開電腦直接寫程式,或許更多時間只是被語法混亂而浪費掉,就如《Best Programming Quotations》中收錄的引言,「程式越早寫,程式寫越久」、「花幾個星期寫程式,可以省下你幾個小時的規畫時間」。面對本質越來越複雜的軟體,定義問題越來越重要,而語言、框架或工具的出現,只是為了在更快地將概念施行在電腦上的一種手段。