Wednesday, August 12, 2009

追求神乎其技的程式設計之道

追求神乎其技的程式設計之道(九)Thursday, August 13th, 2009

追求神乎其技的程式設計之道系列:

最近被很多人提醒這個神乎其技系列停在第八集很久了…(汗)。其實這集草稿已經存很久了,今天總算決定要把它告一段落後貼出來。

先來個題外話,台灣最近遭受莫拉克颱風帶來的巨大災難之苦,雖然我人不在台灣能幫上的忙很小,但還是希望災區的所有人都能平安度過這次災難。

高中到大學

春天的國際科展結束後,我的高中生活也即將劃下句點。回顧高中三年,從校內的資訊比賽一路走到奧林匹亞的國際舞台,在保送大學資格的保護下,到高三又誤打誤撞變成國際科展的代表。雖然從結果看來一切都是如此美好,但在我剛升上高中時,沒有人知道未來會發生什麼事。我爸媽只想要我好好唸書,考上好大學;而我對學校教的科目早已失去興趣,每天都只想泡在電腦前探索這個神秘盒子的內部。於是每次考試結束,家裡就會因為我的爛成績而引發一次大戰,我爸媽也會一再的限制我坐在電腦前的時間。

就像七龍珠裡的標準情節一樣,悟空總是可以和敵人奮戰到垂死邊緣才爆發出最後一擊取得勝利,我總覺得在逆境時才能看出一個人真正的意志力和求生能力。雖然我爸媽能不斷縮減我能用電腦的時間,但他們可沒辦法限制我在學校做什麼或是面對課本時心裡到底在想些什麼。我還記得高一時,我天天都帶著 Introduction To Algorithms(當時還是第一版,很多人叫它白皮書)到學校,不管上課時老師講得多麼口沫橫飛或是下課同學們玩得多開心,我都是看我自己的書。有時碰上管得嚴的老師,我就不看書開始想ACM的題目,用筆在白紙上寫code。我還記得當時為了手邊要隨時有充裕的題目可以想,我把ACM online judge上所有的題目全都印了出來放在一個資料夾裡隨時帶在身邊。

現在想想我那時瘋狂的程度真是難以想像,我當時心思幾乎完全沒放在學校課業上,就連體育課我都躲在樹蔭下想題目的解法。於是到了要升高二選類組時,家裡又爆發了第N次的世界大戰。

還記得升高二前有個考資優班的機會,我爸媽非常想要我去考考看,但我死都不答應。我很堅持的原因是,中一中的數理資優班非常奇怪,進去後就自動變成第三類組,也就是要多念一科生物,但我已經很確定未來我只想念資訊相關的科系,所以我只想選二類,不想浪費時間多念一科生物。就為了這件事,爭執的戰火延伸到我花太多時間在電腦上未來會考不上好學校之類的陳腔濫調上。

台灣父母似乎都想要孩子選擇他們覺得的安全、穩定的路,但我大概天生叛逆了點,說什麼都只想堅持自己的路。不管成功的可能性有多小,或是風險有多大,我覺得我就是應該照著心中的聲音去走。跟他們吵了一陣子後,我決定跟他們立下一個約定:讓我完全自由到高二,如果我沒辦法在資訊比賽拿到夠好的成績足以保送大學,那我到聯考前就都不碰電腦,即使我考不上大學也是我自己的決定所造成的。

從此以後,我就更認真的拼命研究演算法,研究歷年來所有大大小小資訊比賽的題目,只要我醒著的時間,腦袋就全速運轉放在這些東西上。雖然理論上我應該是背水一戰,但那時心裡其實沒有太大壓力,反而覺得可以每天都在玩自己有興趣的東西很幸福很開心,如果比賽結果不好,那可能只是說明我沒有這個天份和這個命而已。

現在回想起來,我覺得當時的我真是不怕死到了極點。要靠資訊比賽保送,至少也要進入奧林匹亞的培訓營,並成為留到第二階段的最後十人之一才有機會(有機會的意思是大學有可能會拒收…)。簡單的說就是實力大概要在全國高中生的前十名就是了。

我想我當時一定沒有想這麼多,只傻傻的做自己想做的事,所以才會這麼有勇氣立下聯考前都不碰電腦的可怕約定。還好我運氣很好,上了高二後從第一場校內賽就把我累積一年的力量爆發出來,就這樣一路打進培訓營甚至還選上國手。

順利拿到保送資格後,父母鬆了一口氣,於是就完全放任我花更多時間泡在電腦裡了。到高三時,我誤打誤撞說要參加國際科展,於是就有了正當理由請公假,各科老師對我的缺席也見怪不怪。高三我幾乎每天都待在教官室裡玩Linux、架各種系統和server來玩,雖說要做科展,但其實也是對什麼有興趣就玩什麼。(可能很多人覺得奇怪,為什麼會待在教官室… 因為教官室是少數有電腦有網路,我又能自由進出的地方。再加上當時有教官找我幫忙做網站,所以….)

到了要畢業的時候,我才發現我高三幾乎都沒踏進教室過,班上的同學我也幾乎都不認識。雖然在自己的世界裡過得很開心,但要畢業時才發現我的高中生活和其他人都不一樣。班上老師們對我也頗有微詞,所以畢業前我還碰到了一個可怕的畢業危機:我高三的學科被當了九科……。(現在想起來還真難想像,到底有哪九科啊orz…)

這件事大概是我一帆風順的高中生活中最驚悚的一件事了。雖然台大已經張開手等我進去,但要是高中不能畢業,那我不就變成比櫻木花道還悲慘的笑話了嗎…。(註:櫻木花道在全國大賽前發現被當太多科無法參賽,所以只好閉關唸書準備補考,當然最後還是順利過關去參賽,不然灌籃高手就畫不下去了。)漫畫中的主角當然不會被要補考這種小事打倒,但現實生活中誰知道呢… orz

在畢業前遭受到如此巨大的危機,實在完全出乎我的意料之外。還好當時帶我們參加比賽的指導老師非常挺我,幫我跟各科老師要一個補考的機會。學校也很好心不打算找我麻煩,就讓我把考卷帶回家寫一天。隔天交出去後我就沒再得知過關於成績的事,我甚至不知道我畢業時每一科的成績,但總之我還是拿到畢業證書了(汗)。

回想起年輕時的瘋狂,真是覺得很不可思議。如果我當時沒有堅持自己的想法,而依照爸媽的意思「好好唸書」,我想現在的我一定過著完全不同的人生,至少不太可能憑聯考進入台大資訊系,而現在也不會在MIT了..。

(待續)

Posted in 追求神乎其技的程式設計之道 | 1 Comment »

追求神乎其技的程式設計之道(八)Wednesday, March 11th, 2009

追求神乎其技的程式設計之道系列:

第八集終於寫完了…。雖然我寒假過得很輕鬆悠閒,但不知道為什麼人就是會在很閒的時候不做平常一直說沒空做的事,然後硬是要在很多作業和工作時開始寫blog….。雖然開學後又開始變忙了,但我到目前還是每個禮拜都跑去滑雪,這樣看來這一系列最後突然會變成追求神乎其技的滑雪之道也說不一定XD

前言夠多了,本集將繼續我在高三時朦朧無知的學習故事…。

Intel ISEF國際科展

第五集裡提到我一直以來都夢想著讓電腦學會自己寫程式。就在我把這個想法的雛型用基因演算法實做出來後,沒想到竟然真的看到了一點結果,電腦真的能透過基因演算法來產生出一個能夠正確運作的程式!

在高三時,已經取得保送資格的我對學校的課業時在沒什麼興趣,於是我想閒著也是閒著,乾脆就把這個點子拿去參加科展看看,這樣一來我就又有理由可以請公假了(笑)。當時似乎是因為已經來不及報名全國科展了,所以我就跑去報名了國際科展,但那時我其實完全不知道這兩種科展有什麼差別。後來查了一下才知道,原來台灣的國際科展除了是一種比賽外,主要的目的其實是要選出代表去參加其他國家(如美國、法國、加拿大…)的科展活動;而一般的全國科展就是一個區域至全國性的展覽兼比賽,在全國選出前幾名後就結束了。

2001年時,那時科教館還在建中對面,那也是我第一次踏進科教館的大門。在展場內把自己的海報貼上看板後,我就到處逛逛欣賞別人的作品。因為資訊科在高中不是正式科目,參加的人比起其他科來說少了許多,但其中還是有些非常驚人的作品出現。當時有個建中的學弟Eric,他寫了一個用類神經網路辨識並動態追蹤影像中人眼位置的程式,於是他就用了個普通的攝影機加上這程式,就變成可以用眼睛控制滑鼠游標的神奇裝置。

除了Eric的作品外,其他的作品看起來大多只是某種現有產品或課本上習題的複製品,並沒有什麼令人特別印象深刻的東西出現。說起來這其實也不奇怪,因為在高中階段要自己學好一個程式語言其實並不容易,在基礎還未打穩前,即使有再多創意也無法發揮出來。而很可惜的是,很多人上了大學終於學會寫程式後,創意和熱情也被磨損的差不多了;而畢業後雖然已經有了基本功力,但卻只能照著老闆開的規格刻畫死板的功能與介面,與其說是程式設計師不如說是程式工匠。

我覺得台灣人的能力並不差,但社會中卻瀰漫著一股抄襲與仿冒的氣息,從最近的酪梨壽司事件(相關抄襲事件還可參考MMDays的整理),可以看出抄襲風氣在台灣並不只是小時候在學校抄抄作業而已;媒體工作者在報導中任意抄襲及轉載是種不尊重自己專業的表現,不但隱含著一種應付了事的心態,更代表著這些人對於自己的工作沒有熱情,更沒有著一點堅持。如果要說我在美國看到這邊和台灣有什麼最大的不同,我想關鍵的差異就在對自己的工作有沒有熱情和堅持而已了。

在評審的過程中,有兩個教授一起來聽我介紹我的作品。因為我沒有任何參加科展的經驗,也沒做什麼講稿或準備就去了,一切只能靠臨場發揮,還好教授們還蠻喜歡我的作品,一來一往的討論之下才讓我不至於太緊張而說不出話來。

自己的介紹結束後,終於鬆了一口氣,教授們對我的作品似乎感到非常新奇和有興趣,後來還問了我「如果再給你兩個月,你能做出更好的結果嗎?」這問題聽起來像是要給我個機會再繼續深入研究下去,難道是意味著要選我當代表嗎?當時我對於得獎其實是沒什麼興趣和期望的,只是想看看別人對這個東西的意見,但如果有人喜歡當然是很棒的事情。這問題無非是給我挑戰的機會,我腦中還有很多改進的想法,如果有更多時間,一定還能做出更棒的結果。所以,我就毫不猶豫的就回答了:「可以,當然可以!」

到了頒獎典禮時,司儀一一宣佈每個學科的獲獎人以及之後要代表台灣去哪個國家參展,同時也頒發一些企業贊助的特別獎。我原本一直以為Eric的作品應該是穩拿第一的,畢竟相較之下我的作品並不夠成熟和完整,甚至連個能稱上科學實驗後的結果都沒有。但這個新奇的點子威力還是很大,我完全沒預料到無心插柳參加科展竟然讓我拿到了Intel頒發的電腦科學最佳獎,並且還選上了美國代表,之後可以代表台灣去美國參加Intel主辦的國際科學暨工程展 (ISEF, International Science and Engineering Fair)。

程式設計之內功心法

得獎當然很開心,這是對於我這個想法和努力的肯定,但隨之而來的也是一股很大的壓力。我得代表台灣站出去,到美國跟來自世界各地的教授、學者、參展代表用英文介紹我的作品,那不能像在台灣這樣輕輕鬆鬆跟教授聊聊天就好,我要用不熟悉的語言跟不熟悉的外國人介紹我這個只花一兩個月做出來的雛型作品… 光想到這個背都涼了。

選上科展代表跟選上奧林匹亞國手是完全不同的感覺。奧林匹亞對我來說是個已經努力很久的目標,我很清楚自己的實力有多少,我對自己花了將近一年所打下的基礎很有信心,而且我對比賽本身已經非常了解,出國比賽不過就是做我已經很擅長的事情而已,一點都不需要害怕和擔心;但選上科展代表真的完全是意料之外的事情,我自覺還沒有花足夠的功夫在這問題上研究,就像一個腳底有油漆還到處亂跑的小鬼,糊里糊塗的鬼畫符被長輩當成畢卡索的作品一樣。

驚恐之餘,我還是警覺到我得開始做很多事來讓我能充滿信心出國去參展。我得趕快把英文練好,至少要能流利的跟人介紹作品,還要能回答各種問題;我還得了解Intel ISEF到底在幹麼,參展代表要做些什麼事;最重要的是還得繼續在這個作品上做更多研究,看能不能在兩個月內做出更好的結果。

參加科展讓我意外領悟到一些事,我發現我高一開始花了一年多投入資訊比賽所得到的並不只是那塊獎牌與一堆獎狀,在練習的過程中,我把台中圖書館所能借到的演算法和資料結構的書全都看過了,就連冼鏡光當初在微電腦傳真雜誌上的專欄也被我從圖書館地下室的陳年庫藏中挖出來,一本一本的影印裝訂起來。我收集了我所能找到的所有大大小小資訊比賽歷年來的題目,加上當時ACM Online Judge上做過的四百多題,我當時參加比賽幾乎都是看完題目馬上就能想完所有可行的演算法和所搭配的資料結構,並用直覺挑出能最快寫出來且又最有效率的解法。除了解決問題的方法外,我也能輕易的把任何想法寫成程式碼,只要能把過程講出來,就能毫不猶豫寫出code來。

這個花了一年多練出來的功夫,讓我到今天都受用無窮。這種感覺就像唸完九陽真經後內力大幅提昇,之後不管再練什麼武功都是易如反掌。在高中時,我改以Linux作為我主要的工作環境,同時也幫中一中架起BBS,並自己架了web server、mail server來玩玩。這些系統底層的功夫乍看之下跟寫程式沒什麼關係,但我後來發現要做各種能在現實生活中應用的軟體系統,總是會碰到這些現有系統的細節問題。

我以前在大學時常觀察別人的project會碰到什麼阻礙,而開發不順利的原因幾乎都是被一些瑣碎的細節所絆住而導致嚴重的進度落後。這些細節都是些小事,甚至跟寫程式沒有直接相關,但總是會讓人陷於泥沼之中。舉一個簡單的例子,假設要在Linux上寫程式需要用到某個library,動手前得先安裝一下。這個看似簡單的安裝其實牽扯到許多瑣碎的細節,像是:distro有沒有提供package可以直接透過網路安裝? 沒有的話就要自己編譯,那就得知道如何用configure、make等工具;編譯中可能還會發生缺少其他相依的工具或package的情形,這時還要能從錯誤訊息中看出到底少了什麼東西,並且想辦法先安裝起來…。這些事情跟寫程式的理念其實一點關係都沒有,但很殘酷的是,有許多人都會被這種細節所絆倒而中途放棄。

紮實的內功和熟悉系統底層的基本武功帶給我非常有效率的實做能力,我可以在想出新的idea的同時馬上勾勒出實做上大大小小的細節;我可以很快設計出核心的高效率演算法,也知道系統各部份的功能有什麼現有的library或系統可以利用,這兩種能力讓我能快速完成prototype。我只怕沒有夠好的點子,完全不會擔心是不是真的能做得出來。

不久前有位數字先生開了一個程式速成班,號稱能在4堂課內教會web程式的開發,只要帶著點子過去就能在上完課後開始創業。我覺得學寫程式本身其實不用很久,我也曾教過一個12歲的小朋友寫程式,兩個禮拜總共約十小時就足夠讓他掌握基本的程式邏輯概念,包括變數、迴圈、if、陣列、抽象化..。但認真說起來這些東西只是基礎內功,有了內功後自己還是要花很多時間去學相關的底層武功,像是要做web程式還得摸熟web server的架設和管理、UINX的shell和系統管理、各種相關網路傳輸協定、資料庫的使用和管理、前端的HTML和CSS設計….。光web程式所牽扯到的實做細節就多如牛毛,如果要在完全沒有穩固基礎的情況下同時學這麼多東西,只要一旦出現問題,一個對系統從上到下每個環節都不熟悉的人是完全沒辦法鎖定問題發生點的。而debug的基本概念就是要先鎖定問題發生的地方,要做到這件事的先決條件就是要很有信心的先排除一些不可能的地方,再做些假設並驗證假設是否成立來判斷可能問題。如果對每個環節都沒有充分的經驗,我不覺得這樣的人有能力清楚的定位出問題所在,更別提是否能獨力完成一個像樣的完整系統。

每個人都聽過成功是一分的天才加上九十九分的努力,如果說天才是能想出絕妙idea的能力,那我覺得還有個關鍵是,要在有點子前先做過夠多的努力打好基礎,等到靈光一現時才能把握住機會馬上實現它。如果等到一分的天才出現時,才準備開始做九十九分的努力,那很容易就會被許多基礎能力不足所帶來的挫折感和障礙所壓垮。

(待續)

Posted in 追求神乎其技的程式設計之道 | 20 Comments »

追求神乎其技的程式設計之道(七)Friday, October 10th, 2008

追求神乎其技的程式設計之道系列:

這次拖稿了很久,雖然下禮拜就要期中考了,但我決定還是要趁這個作業都剛交出去的忙碌低峰期來補上一篇,不然真不知道下一篇要等到什麼時候了…(泣)

思考的高度

上一篇談到了優秀程式設計師的第一要件:「熱情」,這一篇我想要談我覺得熱情之外最重要的能力:「思考」,特別是抽象化的思考能力。

寫程式可以說是一件進入門檻很低的工作,拜現代的GUI開發工具以及大量的open source library所賜,很多低階、跟硬體和作業系統直接相關的細節都被隱藏起來了,所以說其實只要學會某種程式語言並且會把自己的想法鉅細靡遺的轉換為程式碼,就可以說自己會寫程式了。到達這個階段並不困難,只要有心學習的話即使是國中生自己看看書或到巨X電腦上上課都能學會。那麼究竟要如何跨過這個階段,讓自己能和巨X電腦的畢業生有所區隔呢?我認為關鍵就在思考的高度。

寫程式需要的思考能力第一是邏輯思考,主要其實就是用正確、清晰的邏輯表達想法而已,說來簡單但要做好也是需要一定時間的訓練。第二是抽象化思考,這是許多人忽略掉的一點,也是我覺得區隔一個平凡與偉大程式設計師的重要特質。

我覺得所有的程式都可以看成一個巨大的金字塔,頂端是這個程式的最終目標,一個模糊的概念;底部是細節的程式碼。而中間是一個經由不斷切割與抽象化所構成的高塔,每一個程式都是切割為許多的元件、模組,再切為更細的class和function,再來是最底下的變數與邏輯判斷式。

很有趣的是,不同的人看這個塔就會有不同的樣子。初學者看到的塔只有兩層,他們和人溝通的方法是鉅細靡遺的描述程式碼:「我在這裡寫個for,第一次把i設成0,在迴圈內每次檢查這個陣列的第i個元素…」,在他們眼中只有程式的目標和程式碼本身,所以還可能會寫出下面這種讓人哭笑不得的註解:



a = 1;  // 把a設為1



有些經驗後,會再多看到一層,利用function把一段程式碼包裝起來,賦予一個名字和獨特的意義。學會這個後,就可以利用抽象化後的 function名稱來溝通,例如:「我在這個迴圈裡每次都用isCaptial來檢查這個字串是不是都是大寫…」再接下去呢,可以再利用class,利用design patterns,利用更大的模組、子系統來溝通,認真說起來,這其實是一個無止境的切割。

在資訊科學這個領域,抽象化是個無窮無盡的必要行為。因為世間萬物實在太多太複雜,我們只好不斷把東西歸類,並賦予一個名稱、一個意義,經由這樣的過程我們才能用抽象的語言和符號來溝通,避免每次都要從最底層的瑣碎細節開始說起。而平凡和偉大的程式設計師,我覺得他們之間的差別就在於能看到多少這個高塔中間的分層。厲害的高手都很善於切換自己思考的高度,一下能跟你討論高階的系統架構設計,一下又能深入到最底下的組合語言和二進位除錯。他們腦中除了有這高塔每一層的詳盡平面圖,甚至也非常了解不同樓層之間的交互關係。而平凡的程式設計師大多只能專注於自己所開發的範圍,對於其上的架構或其下的細節都不一定能理清頭緒,萬一出現bug也會搞不清楚到底是哪一層出了錯,而被完全無關的細節絆住手腳。

程式語言決定了思考的高度

大部分資訊系學生接觸的第一個語言是C語言,其實我覺得到了21世紀還從C語言開始教是非常值得商議的一件事。我在台大時曾當過兩次計算機概論的助教,雖然大一學生同時還在修計算機程式設計(也就是教C語言的課),但我在課上也同時教他們學Python。

有人問我:「只學C語言不夠嗎?」。如果是為了畢業後能找工作,其實學C就夠了,因為幾乎所有公司都只考基本的C語言能力,也就是說他們認定只要會寫C就能勝任日後的工作。事實上大部分大學都不太教程式語言的,會教C也只是因為大一總得選一個語言教,而C還是老得辣,加上大部分教授也只會這個,所以自然就決定是它了。近年來因為物件導向風行,所以大部分學校還會教個Java或C++,但這也是因為要教物件導向的概念,而不是以教這個語言為目的。除了這兩種外,大概就剩下組合語言了,而這也是因為要教電腦最核心的CPU運作方式,所以才會順便教到的。

程式語言的地位在資訊系其實一直很卑微,大部分教授覺得這只是一個基本工具,就像螺絲起子和鐵鎚一樣。但我一直覺得程式語言是很重要的工具,它不只是讓人用不同語法和電腦溝通,而是讓人能用完全不同的思考方式來解決問題。簡單的說,我覺得程式語言就是決定思考高度的一個關鍵因素,而這也間接決定了寫程式的能力。

舉一個簡單的例子,高階的script語言幾乎都內建map這個資料結構。(也就是一對一的對應表,給它一個key,就能很快的找到其對應的 value。有的語言稱為dictionary、hash、或associative array。)如果寫習慣Python或Ruby的人,一定會很直覺的用map來儲存任何對應關係,甚至用來表示會動態變更欄位的struct。但是,在 C語言裡沒有這種東西,這讓很多只會寫C的人直覺的用陣列加上linear search來存放這種對應關係。如果資料結構學得好的人,會知道這樣寫效率很差,但很多時候因為沒有方便的library,也懶得自己寫一個高效率的 map(不過是存一個電話簿,我難道要先寫一個紅黑樹嗎?),就妥協於沒效率的儲存方法。

這就是一個被程式語言限制住的典型例子。在高階語言用map存東西實在太容易了,所以這會變成思考時的一個小單位,跟人溝通或是規劃架構時都能隨時拿來用。但相反地,在低階語言裡,要有效率又簡單的儲存這種對應關係實在很麻煩,所以人們在思考時會傾向選擇容易的方法來做,而自然忽略掉了以map為基礎的解決方法。

除了script language外,functional language也是另一個進化到神乎其技路上必備的技能。functional language是以function為基礎來思考的程式語言,典型的代表是LISP、Scheme、Haskell。(這邊所說的function是 higher order function,可以以其他function為參數的function,和C語言裡的function是不同的概念。)在functional的世界最棒的特性是程式可以只靠function間的相互組合而生成,不用迴圈不用if一樣可以達成同樣的目的。

舉例來說,如果我要要從一個電話簿中挑出所有姓張的人,並傳回他們的電話,用低階語言(其實我指的是imperative language,但這裡就不要這麼講究了)寫起來大概是這樣:



PhoneData contacts[N] = {…..};

String number[MAX_NUMBERS];

int count = 0;  

for(int i = 0; i ){

  if( !strncmp( contacts i>.name, "張", 1 ) ) // well, 讓我們假設這個strncmp支援unicode

    ret[count++] = contacts i>.number;

}

return ret;



用低階語言寫程式必須不斷處理瑣碎的細節,像是要開多大的陣列、要弄一個額外的counting變數、要用迴圈一個個檢查陣列….。當腦袋裡充滿這些細節時,是很難切換到更高的角度思考的。而functional language提供完全不同的思考方式來解決同樣的問題,以下我用Ruby的語法寫同樣的程式(Ruby具備許多functional language的特性,但不全然是個functional language):



contacts = [ { 'name' => '...', 'number' => '...' }, ... ]

return contacts.find_all{ |c| c['name'][0,1] == '張' }.map{ |c| c['number']}



是的,你沒看錯,就只有兩行,而且真正做事的只有一行而已。這裡用到的是functional language的基本工具:filter(Ruby裡叫find_all)和map。這兩個function特別的地方在於他們能用來取代一般需要迴圈才能做的事,並賦予除了「迴圈」以外更高階的抽象意義。filter的意思是過濾,可以從一個陣列中用一個給定的function為條件來去除不合條件的元素;而map的意義是對應和轉換,可以用一個給定的function作為規則把一個陣列中的每個元素全轉換成另一個樣子。

多了這一層抽象化後,寫程式的思考方式會變得完全不同。迴圈不再只是迴圈,而是可以根據它的目的將之區分為map或filter(其實還有更多,這邊只是先舉兩個做例子),思考時便能以組合這些小元件的方式來構思程式的寫法。這裡提供的不只是語法上的簡便而已,而是整個思維的大躍進,以及思考高度的提昇。

這就是為什麼我要教大一新生Python。Python融合imperative language、object-oriented language、以及functional language,語法簡單清楚威力又強大。雖然他們學過後不見得會繼續用Python,但有了不同語言的概念後,思考的高度會完全不同,寫出來的程式品質自然也不同。

(待續)

Posted in 追求神乎其技的程式設計之道 | 24 Comments »

追求神乎其技的程式設計之道(六)Tuesday, August 19th, 2008

追求神乎其技的程式設計之道系列:

最近新玩具太多,差點讓我的blog被N82系列文章淹沒了。幸虧即時看到qing兄兩篇不錯的文章 程式設計的兩個觀點 (1/2)程式設計的兩個觀點 (2/2),讓我決定還是來早點把這個系列寫完,不然就快變富奸了。

qing兄的兩篇文章指出程式員的兩種型態,一是重視演算法、資料結構、執行效率的「效率魔人」,二是重視程式架構、擴充性、彈性、可理解性的「架構狂」。這兩種人其實都很好,要完成一個偉大的軟體,團隊中兩種人一定都要有。比較糟糕的是,有很多「第三型態人」,他們的信念只有一條:「程式只要會動就好」。第三型態人不在乎效率,也不管架構漂不漂亮,上面要求他做什麼,他就想辦法東湊西湊,從Google找程式剪貼,從MSDN抓範例來用,反正只要能隨便測過一個case就能交差了。

其實第三型態人也不一定是不懂演算法、不懂design patterns,他們常常只是因為火燒屁股了,就不管三七二十一先弄出可以動的程式再說,效率或架構等到下一階段再來改就好…。問題是,下一階段又有新的功能要做,這些人再度面臨抉擇時還是會決定先讓程式「會動再說」。我看過很多各式各樣的程式員,只要碰到這種人,同樣的過程是履試不爽不斷出現。

所以要成為一個優秀的程式設計師的關鍵是什麼?關鍵不在於coding速度有多快、懂多少演算法,或是背了多少patterns,最重要的是「熱情」!

偉大的程式設計師都非常喜歡寫程式,寫程式的過程是一種絕妙的享受,他們執著的地方或許不同,可能是程式的效率,也可能是開發的效率,甚至是架構的彈性或是程式碼的精簡美觀程度,但他們都非常想要並堅持自己應該寫出「好程式」。熱情能驅動他們把軟體的某一個面向雕琢到極致,這需要超乎常人的毅力和堅持,以及絕不向壓力妥協的精神。只要具備這種熱情,不管你在乎的是什麼,都可以成為一名偉大的程式設計大師。

P.S. 雖然這篇文章講的東西很八股,但我發現這真的非常重要,看一個人的熱情就能知道他做出來的成品是什麼樣子。如果是我來面試,我一定會在面試時觀察這人有沒有喜歡寫程式的熱情,沒有熱情的人容易向現實壓力低頭,也不會要有不斷精益求精的信念,在如此競爭的時代是很難生存的。

P.S.2 要追求神乎其技前,當然要先知道自己的目標是什麼樣子,所以我本來想在這篇寫一個優秀的程式設計師應該要有的特質和能力,但才寫了第一項就落落長。所以還是等待下一篇再繼續這個主題好了。(路人:「這不就是擺明要當富奸嗎!」)

(待續)

Posted in 追求神乎其技的程式設計之道 | 12 Comments »

追求神乎其技的程式設計之道(五)Friday, July 25th, 2008

讓電腦自己寫程式的夢

在高三突然對於讓電腦自己寫程式產生興趣後,我每天就都想著這件事,夢想著未來某一天的電腦能跟我一起寫程式,當我把程式目的說給它聽時,它就一邊把細部的程式碼產生出來:我不用想如何命名變數,或是要用什麼演算法,還是我到底要不要把這個功能變成獨立的class…;我只要動動嘴巴:「我想寫一個橫向捲軸的射擊遊戲。玩家操縱著會變形的飛機,還有四種武器。第一關要長這樣這樣……」,然後電腦就幫我把遊戲寫出來了!如果真的可以這樣,那該有多好啊!(如果真的成真,恐怕我也要失業了?)[註一]

這個想法實在太讓人興奮了,雖然說這是一個遙遠的夢想(其實那時候我並沒有覺得這麼遙遠,只能說自己太不自量力XD),但也開啟我對於人工智慧、程式語言結構、軟體工程等領域的高度興趣。

寫程式很難?

在高中時代頗不知天高地厚的我,一邊嘗試著利用基因演算法(Genetic Algorithms)讓電腦自己產生程式碼,一邊研究各種程式語言的結構和特性。靠直覺摸索出利用tree作為中介來描述一個程式後(那時沒唸過 compiler,不知道這個其實就是Abstract Syntax Tree),我突然就對這個想法的可行性信心大增,所以一時衝動就去報名了國際科展,打算把這個想法實做出來。

真的做了以後才發現,最難的部份不是產生出程式碼。如果你把程式語言的基本元素,像是if、for、變數、運算符號等東西做成一塊塊磁體,然後拿給一隻猴子玩,那麼牠其實也能拼出一堆程式給你,問題是:「你要怎麼知道這些程式碼真的達到了你想要的目的?」

悟出這個道理後,我突然了解寫程式最難的部份是在驗證程式碼真的跟你所想表達的事情完全相同。(所以說我對於各家軟體公司的QA地位都低於RD其實感到很不平)

基本上,我們只能設計大量的可能輸入值丟進程式裡,並比對程式跑出來的結果和我們想要的輸出相不相同。即使在這種情況下,我們也只能說這個程式在測試過的這些輸入值上所產生的結果是正確的。也就是說,除非我們測試過所有可能的輸入(這意味著無限多種可能),不然永遠沒辦法知道某個程式是對還是錯。除此之外,即使只測試一個輸入值,也還有個很嚴重的問題:「我們怎麼知道這個程式要跑多久?」換句話說,當你程式跑下去,你怎麼知道他是掉入一個無窮迴圈,還是其實正在拼命計算當中?

我在參加國際科展時,認識了歐陽明教授,他告訴我這個問題叫做 Halting problem,Alan Turing在70年前就證明了這是一個無解的問題。知道這件事後,才發現自己所知實在太少,對於資訊科學的基本知識實在非常不足,但這次經驗其實也給了我一個明確的方向,讓我把書中的理論和實際的目標連結在一起。

見樹又見林的學習之道

提到書本中的理論,有很多人問過我要怎麼學習寫程式或資訊相關的知識,我順便在這邊分享些心得給大家參考。

常看到許多人抱怨大學裡學的東西都是理論,畢業後找工作時才發現什麼都不會都得重學;當然企業也會抱怨,大學應該多教一些實務課程,不然出社會後還得重新訓練。說來說去,一致的口徑指向理論和實務是打死也扯不上關係的樣子,尤其念資工的人更常這麼說:「學校為什麼不教C#?為什麼不教我做網頁?好歹也要教個HTML嘛!」







我覺得學習任何事物,一定要有充分的興趣才會有效率,在不知道所學為何的情況下被逼著學習是非常痛苦且沒有效率的。最近看了一本書:沒有資優班,珍視每個孩子的芬蘭教育,書中提到芬蘭教育成功的秘密在於「見樹又見林」,這句話也是我對於如何學習最想分享的秘訣。

台灣的教育方法是「先見樹,再見林」,也就是先教你細部的方法和技術,等你都學會之後(或是硬背起來之後),出社會後就會知道為什麼要學這些東西。(很多人小時候都聽過「等你長大就知道唸書有多重要」吧。可是現在比較多人畢業後反而說「我不知道之前念那麼多書有什麼用」)在這種體制之下,許多人在還沒見到整片森林的美景前就被一棵棵大樹搞得暈頭轉向,痛苦萬分,在不知道「學了這個可以做什麼」的情況下,不管學什麼都會覺得沒有意義沒有動力。

而「見樹又見林」的學習方式,是先找到能引起自己興趣的目標,讓自己有個理由去認真學習,之後再往細部的技術和理論去學習。我從小就很想自己寫遊戲,為了達成這個目標,我就四處尋找相關的資料,慢慢的我就知道自己應該要學好一個快速的低階語言(像是C++),如果要寫繪圖引擎可能還得學一點圖學的理論和技術,如果要做網路連線還得學網路相關的技術…。

這裡有個重點是,不要看過森林後就忘記它,而又迷失在幾棵樹幹上,要讓自己一直重複見樹又見林的過程。

這跟如何有效開發軟體的秘訣是一樣的。一個有效率的軟體開發方式是用 iterative process,把包含設計、實做、測試的iteration時間縮短,但要一直不斷重複這個iteration無數次來改進現有的成果。同理,在已經有目標的狀況下學習時,每當學會一些新東西,就要馬上試著把它實現出來,即使是只有幾行程式碼的prototype也沒關係。只要一直不斷的學,這個prototype就會一直被改進,一直加進最新學到的知識和技術而更接近最終的目標。

同時動腦也動手(註二),用這種角度來學習就能充分了解自己學會的東西可以用在什麼地方,馬上得到回饋的成就感還會刺激自己繼續向前進,形成一個非常有效率的學習循環。用這種角度看學校教的東西,就能知道書本上的理論可以用在哪,並且又欠缺哪些實務知識讓自己無法做出想要的東西。

這方法理論上可以擴展到任何事物的學習,重點在找到有興趣的目標和書本裡知識的連結,我覺得這是老師應該要出力的地方,無奈的是台灣的教育體制把中小學老師們變成出考題和改考卷的機器人….。

註一:

其實,現在真的有這樣的研究,而且驚人的是目前已經有了非常接近我想像中未來的雛型。最讓我興奮的是MIT Media Lab的Hugo LiuHenry Lieberman做的 Metafor:只要對電腦用英文描述你要的程式,電腦就自動把Python code生出來給你….。

註二:

話說, MIT的校徽上就是一個拿著鎚子的工匠和一個拿著書的學者,所代表的意義就是 “Mind and Hand”,也就是期望每個學生都能手腦並用,除了做夢外也要實做出來才算數啊。

(待續)

Posted in 追求神乎其技的程式設計之道 | 13 Comments »

追求神乎其技的程式設計之道(四)Tuesday, July 15th, 2008

程式設計到底是什麼?

2000年IOI在北京舉辦,這年台灣的代表隊成績還算不錯,拿到三銀一銅,比較可惜的是我第一天表現不理想而落到了銅牌,雖然不至於兩手空空無顏面對江東父老,但也知道自己的實力大概就在銀牌和銅牌的邊緣處吧。IOI結束後,我又回到了學校,但因為已經取得大學保送資格,在學校其實也是輕鬆寫意,成天就看自己的書或研究自己有興趣的東西。

在這段時間中,我開始有所警覺,我發現我雖然很會寫程式解題,但那都是一兩百行以內的小程式,真實世界的程式根本不是這個樣子的!雖然我能很快看出一個問題該用什麼演算法效率最高,並且在很短的時間內把自己的想法正確地轉換成程式碼,但我還是不知道市面上的軟體或遊戲是怎麼做出來的。

我這時才開始接觸C++和物件導向的概念,我突然發現要寫個大程式還真不是簡單的事,除了程式語言外,還有好多瑣碎的函式庫得學。像是要畫圖就要學 2D的SDL或是3D的OpenGL,要做Windows GUI程式就要學Windows SDK或是MFC,要寫網路連線就得學socket,要讓遊戲執行順暢甚至得用組合語言寫某些部分…。好多好多東西不斷湧出來,學這些東西很有趣,因為我一邊學就會一邊聯想到學會這個功能後可以用在遊戲裡的什麼地方,於是整個學習過程就像把我夢想中的拼圖一塊一塊拼上去一樣,非常有成就感。

邊寫這種實用性的程式時,我也發現以往在比賽中累積了很多不好的習慣,像是濫用全域變數、變數隨便命名、把整個程式塞在main裡…。這些壞習慣在寫小程式看不出來有什麼差別,但隨著程式規模變大,這就變成了很致命的習慣。而這種習慣一但養成,之後會變得更難改,所以強烈建議初學程式設計的朋友們,一開始就不要偷懶,從認真幫變數想個好名字開始吧!

這段期間也讓我想了很多關於程式設計的有趣問題,像是寫程式到底算是科學+工程,還是藝術?寫程式必須要非常非常精確,任何一個字打錯都可能會讓整個程式跑出完全不同的結果,這對於天生就容易犯錯的人類來說實在是艱鉅的挑戰。為了避免錯誤太多,我們只能用一些固定的流程並強迫程式設計師遵守,讓可能的錯誤減到最低,這就是所謂的軟體工程。雖然有工程的影子,但寫程式卻是很難精確管理的工作,因為面對同樣的問題,不同的人絕對會寫出不同的程式,甚至是提出不同的解決方法﹔有的程式可能要跑三天三夜,有的程式卻能在瞬間得到正確解答﹔有的程式碼雜亂不堪,也有的程式碼井然有序清晰易讀﹔有的人要花三天寫 1000行,也有人能在一天寫100行就達到完全相同的效果﹔這些程式的目的可能完全相同,但呈現方法卻有千萬種,軟體工程難道可以限制每個程式設計師大腦運作的方式和速度嗎?

從程式碼的觀點來看,不同的人寫出的程式碼也一定不相同。從程式碼的排版、命名、段落安排、抽象化程度、運作流程可以看出作者的個性、態度、思考邏輯及深度。從這個角度來看,寫程式更像是種藝術,就像是畫筆或樂器一樣是一種表達自我並將思想具體化的工具。

另外我很感興趣的是,人一定要寫程式才能叫電腦做這麼多複雜的工作嗎?能不能教電腦寫程式,讓人只要告訴電腦要寫什麼樣的程式就好?或是有沒有更簡單更方便的方法能和電腦溝通,並且保有同樣的控制力?

就在被這個問題困擾著的同時,我意外從一本書看到基因演算法(Genetic Algorithms)這個名詞。稍微研究過後讓我大吃一驚,因為我發現基因演算法是一個超級有效率的搜尋演算法,可以在幾近無限廣大的可能解裡面很快找到接近最佳解的答案。所以,我很快想到了,如果想要讓電腦寫程式,其實就是告訴他要寫的程式要達到什麼目的,並讓他在幾近無限大的可能程式中找出能跑出我們需要答案的那個程式。這是一種把寫程式視為搜尋的概念,我當時想到這件事非常興奮,但我並不知道其實早就有人想出同樣的概念(這叫Genetic Programming),並已經做了許多研究。

其實有時候無知是件好事,這樣才會有勇氣在不知道這個問題有多難的情況下去嘗試看看。如果我當初就知道這問題其實是能拿好幾個博士學位甚至是得到圖靈獎(Turing Award, 資訊界的諾貝爾獎)的難題,我可能連繼續嘗試的勇氣都不會有了。

(待續)

Posted in 追求神乎其技的程式設計之道 | 4 Comments »

追求神乎其技的程式設計之道(三)Saturday, July 12th, 2008

勁敵

熱血的少年漫畫都有一種標準公式,熱血但什麼都不懂的主角,加上一個天才勁敵的刺激,讓主角能在不斷遭遇的困難和挫折中不斷爬起來進步。就像火影忍者中的鳴人和左助,或是棋靈王中的進藤光與塔矢亮,要進步最快的方法就是找到一個遠遠超過自己的勁敵作為目標並且努力打敗他。

我在高一時參加校內的資訊能力競賽初賽,在都還搞不太清楚要比賽什麼東西時就去參加了。當時只會用BASIC,知道要用筆寫程式時還嚇了一跳,還懷疑閱卷老師難道能在頭腦裡執行程式嗎? 我記得那題目不難,我每題都有寫,但最後只拿了個佳作,離學校的代表隊還遠得很(話雖如此,我也是唯一有得獎的高一生了)。過了幾個月,我非常驚訝的發現台北市的資訊能力競賽,竟然有一個建中的高一生SBB拿了一等獎,更可怕的是他接下來又在全國能力競賽拿到二等獎,才高一就已經有能選上奧林匹亞國手的氣勢,前途無可限量。

受了他的刺激,我非常拼命學習,先花一個月自己把C語言學起來,接下來就到圖書館借回所有有關資料結構和演算法的書,每天都拼命看。這是我進步最快的時期,當時我在家自己跟著已經進入IOI培訓營的選手們一起練習ACM Online Judge上的題目。每天到學校都在看Introduction to Algorithms,因為沒有電腦,只好用紙筆練習解ACM Online Judge上的問題。白天在學校想解法,回家就在電腦上把程式寫出來,並submit上去看看結果對不對。透過這種方式練習,可以看到自己解出問題的數量不斷增加,強烈的成就感能刺激自己不斷練習不斷思考,如此循環之下真的進步得非常快。

很快地,在我升上高二後,我發現我已經寫了三百多題,進入ACM Online Judge世界排行榜的前幾名了。同時我也發現有另一個跟我一樣每天都增加好幾題的人,沒想到竟然就是跟我同年的建中SBB。雖然我每天都能寫兩三題,但 SBB的解題數仍遠遠在我前面,逼得我只好印出所有題目,帶去學校不管上課下課都在紙上解題。就這樣我們的差距慢慢拉進,不知不覺我們也站上排行榜的前兩名。如果我今天多寫一題,我就能暫時站上第一,但隔天馬上又會被他超越過去,彷彿他早就已經囤積了數十題起來等著慢慢折磨我一樣。

就在這樣的刺激下,我就像漫畫中的熱血主角一樣快速成長,進入了勁敵所在的境界。我順利從校內初賽、中區預賽,一路拿第一進到全國決賽。非常戲劇性的是,在這年全國能力競賽,SBB拿了滿分得到第一,我錯了半題排名第二,而全國能力競賽一向是前兩名都為一等獎,也就是說我和SBB原本應該都是一等獎,沒想到評審說我和第一名的滿分有個落差,所以一等獎就改成一名,而我就變成了二等獎第一…(實在很嘔,但也不能怎樣Q_Q)。比賽雖然輸了,但我沒有很難過,因為我知道自己還能再進步,還能變得更強,繼續努力下去我很可能可以選上奧林匹亞的國手。

國手之路

高中各學科的奧林匹亞是每年一度的國際盛事,奧林匹克運動會比的是人類體能的極限,而各學科的奧林匹亞比的則是運用腦力的極限。我第一次是從YJL那聽到這比賽名字的,但那時覺得非常遙遠,因為我看到連他這麼厲害的人都選不上國手,可見這不是一般人能輕易踏進去的領域。

改變我想法的是中一中穿堂的一張照片。中一中有個不錯的傳統,只要代表學校參加比賽或科展獲獎,學校就會把獲獎學生的照片掛在穿堂的榮譽榜上。還記得高一時在穿堂閒晃,一個個欣賞歷屆學長們偉大的功績,突然間發現有個叫CLK的學長厲害得不得了,不但在中區能力競賽拿第一,到了全國也還是第一,而且他還選上了97年IOI的國手。後來我才知道他以前也是中一中電研社的社長,而那屆的副社長也非常厲害,自己寫了一個microkernel作業系統代表台灣去美國參加國際科展。有這些如傳奇一般的學長,給了我很大的激勵作用,我這時突然覺得自己或許也能像他們一樣有照片被掛在上面的一天。現在想起來,還真不知道當時自己怎麼能這麼有信心,其實有種不知天高地厚的感覺。但或許也因為如此,才沒有被這種「看起來很困難」的目標嚇到而連嘗試的勇氣都不敢拿出來。

很有趣的是,在我剛上高一時,CLK是中一中第一個也是唯一一個資訊國手。但就在我高一下的時候,有三個高三的學長竟然同時選上99年IOI的國手。因為一年資訊國手名額只有四個,一直以來都是建中學生呼聲最高,這年我們一口氣拿下三個名額簡直是不可思議的奇蹟。我也因此而信心大增,彷彿我每天在他們旁邊練習也能受到逸出的強者氣息感染而變強一樣。

就在我在全國能力競賽拿到第二名後,我就開始為IOI培訓營做準備。IOI培訓營大約收30人,要關在師大內集訓四週,每天都請教授來上課,每週進行一次模擬考。第二個禮拜結束會先淘汰一次,留下來10個人再繼續廝殺,直到四個禮拜結束。培訓營過後不是馬上就能知道成績,還要過一段時間才會知道四個國手是誰,但因為留到第二階段基本上就等於有了保送任何大學資訊系的資格,所以第二階段的氣氛其實還比較歡樂一些。

在師大集訓的四週讓我認識很多在這個領域頂尖的強者們,我覺得這更勝於實際上在課堂上學到的東西。這些強者們如今都還持續在資訊界的各個領域活躍,常常覺得不管到哪參加活動或研討會都還是可以碰到這些有相同背景的人,可見這個培訓營真的也訓練出許多資訊界的中堅份子。

在集訓時,大家也不是每天各自悶著頭寫程式。其實很不好意思說,當時在培訓營進步最多的可能是魔法氣泡對戰的戰術…。雖然有電腦就會被拿來玩電動,但培訓營的學生還是比較特別。每年培訓營都會流行一些能用程式寫AI來決鬥的遊戲,像是坦克大戰、俄羅斯方塊等等,我們那年流行五子棋,大家利用空閒時間寫五子棋的AI,碰在一起時就讓各自的程式互相決鬥分個高下,玩起來比自己親自下去玩還刺激很多。

經過四個禮拜的集訓,每天討論演算法,每天寫程式,到最後連睡覺都會夢到程式碼。當時因為才高二,也不會覺得壓力很大一定要留到第二階段甚至要選上國手,一直抱持平常心反而意外表現得不錯。結訓過後,我每天沒事就盯著培訓營的網頁看,希望能趕快看到國手名單公佈。就在名單公佈的那天,我盯著螢幕驚訝的說不出話,我竟然真的選上國手了!這真的是平常完全不敢想像的事情,真的沒想到我竟然也有一天能讓自己的照片被掛在中一中的穿堂上….。

(待續)

Posted in 追求神乎其技的程式設計之道 | 11 Comments »

追求神乎其技的程式設計之道(二)Monday, July 7th, 2008

(本來沒有想寫這麼長的,哪知道一下手後欲罷不能…。看來這系列文章會變成長連載了。)

資訊奧林匹亞與程式競賽

在升上高中前,我因緣際會透過一個國中的同班同學認識了他的哥哥YJL。YJL比我大三年,我要進中一中時他剛好畢業,很巧的是他很會寫程式,一直都是中一中的資訊能力競賽代表隊成員。剛認識他時,他就demo給我看他用QBASIC自己寫的橫向捲軸射擊遊戲,當時看得我目瞪口呆,我完全沒法想像 QBASIC竟然能寫出這麼順暢且華麗的遊戲。這個demo如果不說,我一定會以為這是市面上在賣的商業遊戲(我那時還以為一定要學C才能寫出這種遊戲)。

透過YJL我也得知原來高中還有資訊能力競賽和奧林匹亞這種比賽,聽他述說跟全國的高手一起比賽寫程式時,讓我不知不覺也熱血沸騰起來。我把他高中時留下的各種參考資料全帶回家,並透過他認識了更多還在中一中的強者學長們,就這樣在踏進高中校門的同時也決定了我這三年的方向。

這裡我先介紹一下對於高中生最重要的資訊比賽。高中的學科能力競賽是教育部主辦的比賽,包括數學、物理、化學、生物、地球科學、資訊,每間高中通常會先辦個校內初賽來選出代表選手,再由這些選手參加各區域的能力競賽,最後各區的前N名(每科的人數不同)才能參加全國競賽。以資訊科來說,我記得是校內取6名,中區再取6名進全國決賽。到了全國決賽能拿到前10名,還能直接保送進資訊奧林匹亞的培訓營,不用再另外參加培訓營的入營考。至於資訊奧林匹亞 (International Olympiad in Informatics, IOI)則是國際性的資訊比賽,參加的人是從每個國家挑選出來的頂尖高中生,每年輪流由一個參賽國舉辦,選手要進行兩天每次連續五個小時的頭腦比賽,其中只有一半的人可以得到獎牌。

這些比賽和一般的程式比賽其實很不一樣。這種比賽比的是解決問題的能力,不是比賽軟體實做的能力。題目比較像數學問題,只是除了紙筆外,還得用某種程式語言實做出能解出正確答案的程式,也就是說參賽者必須想出問題的解法(演算法),再透過自己擅長的程式語言寫進電腦,讓電腦執行後輸出問題的答案。這種解題比賽主要考驗的是運用資料結構和演算法以有效率的方法解決問題,並寫出正確程式的能力。大學有個類似的比賽是ACM ICPC,問題類型和IOI很類似,但比賽的模式和方法則有很大差異,有興趣的人可以自行尋找相關資料。雖然IOI是給高中生參加的比賽,但IOI題目的水準其實非常高,如果你能輕易解出IOI的問題,那… 我跟你打賭去Google面試也有99%的機率會通過。順便一提,Google Code Jam就是一個開放給所有人參加的解題比賽,題目類型就跟IOI和ACM ICPC差不多,前一百名就有獎金,還能免費去Google Mountain View總部玩一玩喔。

參加比賽是一個評估自己實力的好方法,沒在比賽會場上較勁過,真的很難體會解題與寫程式能力的差距可以有多麼巨大。一個頂尖的程式設計師和一個普通的程式設計師,其生產力是很輕易的能有十倍甚至百倍以上的差距,而寫出來的程式碼品質及效率也是同樣會有如此巨大的落差。

透過良性的競爭,高中這段時間也成了我進步最快的一個時期…..

(待續)

Posted in 追求神乎其技的程式設計之道 | 13 Comments »

追求神乎其技的程式設計之道(一)Saturday, July 5th, 2008

最近有讀者問到我學寫程式的方法和經驗,讓我一下掉入時光隧道回想起當初用VB寫出自己第一個遊戲時的成就感,但當初沒料到的是我真的就此迷上了電腦和寫程式的快感,不知不覺也過了10年的光陰…。在這篇文章中,我想寫出我對程式設計的看法和我一路學習上來的歷程和經驗。寫程式是一條無止境的道路,不只是科學和工程,更是一種藝術。而我還在追求「神乎其技」的半路上,雖然還有很多要學的,但我也希望能讓初學者更容易看清楚這條路是什麼樣子,避免陷入盲目追求新技術的死巷中。

一切的開始

如果是從DOS時代開始玩電腦的玩家,應該都知道當初DOS有兩個內建的QBASIC小遊戲:貪食蛇和猩猩丟香蕉。這兩個小遊戲是許多人兒時共同的回憶,我還記得我國小時曾有幾堂電腦課,當時老師在台上嘰哩瓜啦的不知道在教什麼,而台下每台電腦都是貪食蛇或丟香蕉的畫面(老師對不起,其實我就是帶頭做亂的罪魁禍首…)。

微軟把這兩個QBASIC遊戲附在DOS內對我產生了莫大的影響,那是我第一次發現到原來QBASIC和不只是像PE2能打打字而已,QBASIC 竟然能把一堆看起來像咒語的文字變成遊戲!幸運的是我家剛好有本第三波的QBASIC入門書,沒事我就自己拿起來翻著看,雖然當時太小,即使把整本都看完了還是搞不懂貪食蛇是怎麼寫出來的,但也誤打誤撞知道了原來這就是程式設計,原來我能直接把貪食蛇檔案內的一個數字改掉就能有幾百條命可以死,原來學寫程式就能做出電腦遊戲…。對小孩子而言,知道這些事就像告訴他魔術師袖子裡的秘密一樣,我一天到晚興奮地要老爸帶我去書局看電腦書,彷彿真的可以搞懂電腦螢幕背後的一切魔法一樣,我也夢想著有一天能寫出自己的遊戲。但當時我沒想到的是,我還真的花了十幾年的時間在探索電腦的魔法…。

MUD與黑白棋

升上國中後,家裡裝了一台28.8kbps的modem,當時的internet還沒完全成形,在沒有Google的時代internet是沒什麼價值的。當時的modem最常被我拿來上一些撥接式的BBS,那時候的撥接BBS站台還不少,最棒的是還能從站上抓到很多軟體和各式各樣的教學文章,像是如何用組合語言寫電腦病毒,如何破解大富翁2之類的文章。這些文章對當時的我就像武林密籍一樣,雖然沒辦法完全看懂,但我也是從中得到很多零碎的概念,像是16進位的換算、組合語言、中斷向量、常駐程式….。

在國二時,我還不小心迷上當時一個超熱門的MUD – 萬王之王(KK),每天放學回家都急著連上線,讓家裡電話整晚都忙線中,玩到每個月電話費都是上千元,搞得我媽數次警告要把modem收起來再也不讓我上網了。(還好她沒真的這麼做,不然我現在就沒辦法寫這篇文章了。)

MUD是現在MMORPG的純文字版,整個虛擬世界都用文字描述,並且只要用telnet就可以連上去玩了。但內行的玩家都知道,玩MUD應該要用 zMud或是UNIX下的tintin++,因為這兩個軟體可以設定所謂的trigger,偵測到某些事件的發生,就能自動採取事先指定好的動作。因為一切的訊息都是由文字呈現,所以偵測事件非常簡單,只要看看有沒有特定字串出現就可以了;而要做特定的動作也很簡單,就是送出文字指令而已。(眼尖的人一定會發現,這其實就是現在MMORPG外掛的最原始形式。)嚴格說起來,zMud是我首次寫「實用程式」的平台,我學會透過trigger在MUD的世界中寫自動化的機器人,自動在迷宮中遊走,自動換裝備打怪練功..。這時的我突然體會到,會寫程式真是太棒了,我在MUD中簡直跟神一樣。其實當時我也不過只會用最基本的變數、if、迴圈而已,但透過在虛擬世界中寫機器人的練習,讓我的邏輯思考概念有飛快的進步,也給我了非常強烈的動力想好好學一個正統的程式語言。

升上國三後,很幸運的透過推薦甄試提早上了台中一中,升學壓力解除後,老師和父母就完全不管我要幹麻了。這時我終於有了一段完整的時間可以好好的再把BASIC重新學過,無奈的是在我國三時QBASIC已經快滅絕了,取而代之的是Windows上的Visual Basic,我只好硬著頭皮買本新書來從頭學起VB。當時我看的是王國榮的VB 5入門書,整本書有六七百頁吧,比我國三所有課本疊起來都還厚,現在想想小時候真的有點不知天高地厚竟然相信自己能看完這麼厚的磚頭書。那時候我每天上課就帶著這本磚頭去學校,這樣看了幾個禮拜下來,沒想到我這時突然都看得懂了,很多原本不知道有什麼用途的概念突然都相互連結起來了。(多虧了在MUD裡的訓練!)就這樣,某天突然有種打通任督二脈的感覺,我發現我全搞懂了,迴圈、陣列、Windows GUI控制項、去背貼圖…,我突然想通要怎麼用程式語言寫出遊戲了。

從那之後,每天回家就是打開VB寫程式,我想寫個黑白棋來檢驗自己的想法,我把自己知道的所有概念都放進去,有GUI元件、有貼圖、有動畫、有音效..,這是我第一個完整的程式,從頭到尾每一行都是自己寫出來的。(以現在的眼光來說只能說是一個期末project規模的小程式,但對當時的我可是意義非凡)

這個黑白棋讓我印象最深的其實是debug的痛苦經驗。我花了一個禮拜把程式的核心部分完成,但在吃子的時候卻跑出一個不明的bug會打亂整個盤面。為了找這個bug,我又花了一個禮拜,每天從早到晚都在想哪裡寫錯了,後來慢慢trace了好久,才發現竟然只是一個變數忘了歸零!!!

這種bug很常見,不過只是programmer最容易犯的無心之過之一,但這件事對我的影響非常大,它讓我花了很長時間在想以後要怎麼避免犯同樣的錯。我後來才知道一個普通的programmer和厲害的programmer從這裡就會分出高下:普通programmer犯了這種錯會覺得很平常,並提醒自己下次別再這麼笨了,但實際上不久後一定又會再犯同樣的錯;厲害的programmer會反省自己寫程式的方法,並改變原有的方法或習慣來避免以後再度產生同樣的bug。

古老的程式設計教材(尤其是C語言),都說要把變數宣告在函式的一開頭,並且因為變數宣告完還得經過初始化,所以很多人習慣是在函式開頭宣告並初始化所有變數。這不是錯的,可是,這其實就是會導致bug的元兇。

因為變數在開頭就被初始化,這樣在真正要用到它的時候就能直接拿來用,但是如果這個變數需要被歸零(也就是重新初始化)並在迴圈中重複利用,就很容易會忘記要再多做這一步。(在多層迴圈中更容易發生)

我為這個bug苦惱了幾天,後來才意識到這是coding style的問題,只要改變宣告變數的習慣,就能避免犯這種錯誤。如果一開始就在迴圈內宣告並給定變數的初始值,而不是在函式開頭宣告,就不會有這種 bug跑出來了。有了這個經驗後,我歸納出一個原則:「永遠在變數需要被用到的最內層區塊才宣告並初始化該變數。」這種原則很重要,我日後一直放在心裡,它也幫助我避免掉未來再犯同樣錯誤的可能。(事實上,我後來再寫了十年的程式,再也沒有比這更痛苦更長久的debug經驗了…)

(待續)

Posted in 追求神乎其技的程式設計之道 | 17 Comments »

No comments: