非科班出身的人學習編程不負責任指南
為何要寫這樣一篇文章
來我們這個實驗室里讀研的學生可能自從來到這里的第一天就覺得自己的命運很苦逼。他們讀本科時主修的是機械設計、制造以及自動化之類的專業,畢業時的簡歷上也頂多是寫寫擅長 MS Word、PowerPoint、UGNX、AutoCAD 之類的應用軟件。他們有限的學習生涯里,怎么也不會想到來到這里竟然要首先重新學習 C 語言,然后還要學 C++,接下來還要學習 Python 或 Lua 什么的,而且竟然還不讓用 Windows,只能用連個 QQ 都沒有并且經常出故障的 Linux……
在他們心里,編程似乎并不是多么有趣的事,所以他們就覺得編程很難。對此我有同感,大家都會玩的 Windows 里的挖雷與紙牌那樣的小游戲,還有很多人會打的麻將,還有圍棋,這些事我覺得也挺難的。
我曾經告訴他們,如果不會編程,那么他們就很難解決自己的研究方向上的那些問題,因此也就不可能寫出有價值的論文,不可能順利的畢業拿到學位,不可能找到很好的工作……這種功利性的『威脅』,對于有些人會有點效用,但是如果沒有能力讓他們自發的對編程產生足夠的興趣,這對于任何一個『好為人師』的人而言都是一種莫名其妙的羞辱。
現在我試著去告訴他們,編程不僅不難,而且會很有趣,其中充滿著許多值得深思之處,而這些深思對于我們人生也會產生許多增益。于是,就有了這篇文章。
編程是什么
無論你是不是程序猿,每一天你都在編程,每一天你都被編程。編程,就是設計一些步驟,組織這些步驟,讓這些步驟在當前環境中正確的運行,最終得出自己想要的結果。
你的每一天都是在起床、喝水、吃飯、工作、上廁所、娛樂、睡覺等步驟的有序組織下運轉的,你活在這個程序中。這個程序可能有一部分是你的領導編制的。如果你當了領導,你也可以為你的員工編制一些程序。
機械設計,其實比編程還要編程。所謂的機械零件,就是數據結構。所謂的傳動機制,就是應用程序接口(API)。所謂發動機,就是程序的內核。你將零件裝配好,通過傳動機制將它們接駁到發動機上,于是你就創造出來一部機器,通上電或者打著火,就可以讓它運轉起來。但是,編程要比機械設計來的更為簡單,你不需要經常給自己所編寫的程序添加潤滑油,也不需要去對每個數據結構進行復雜的力學分析,更不需要關注這些數據結構是否嚴絲合縫的相互配合,至少目前的計算機軟件工程是這個樣子的,它不像機械工程學科那樣以堅實的物理定律為基礎。機械的結構與運行規律總是可計算、可分析的,而軟件的結構與運行過程卻充滿著太多不嚴格的環節。這種不嚴格,卻給我們營造了一個可以發揮天賦或工科實踐經驗的空間。顯然,即使軟件工程存在著各種不嚴格,但是我們卻能夠通過編程模擬出機械工程的一切。事實上也是如此,現代的機械工程領域,軟件已經無處不在。
機械設計有很多精妙的『算法』,像縫紉機、槍械、發動機之類的機構,設計它們其實要比計算機世界里的算法設計難得多,而且這些機構對人類文明的發展往往能夠產生巨大的推動作用。希望你不要因此愛上機械設計……顯然學會編程,你會對機械設計的理解更為深刻。因為編程是將『設計』本身作為一種智力活動而對待的。 你可以將機械工程領域的那些智力活動應用于編程,也可以將編程中的智力活動應用到任何設計之中 。
入門書
學習編程之前,應該先問自己一個問題:我為什么要學習編程?不要打我……我知道你們是被逼著去學習編程的,那就不妨被逼著思考『我為什么要學習編程?』。
如果不知道答案,也沒有關系。反正這個問題與學習編程也沒有太大關系。其實,我們已經做了非常多的不需要回答為什么的事了。我們連『我們為何而存在』這樣的問題都不知道答案,卻依然糊里糊涂的活到了現在。
先推薦幾本入門書以及 閱讀它們的方法 ,因為學習編程最不需要的方法就是將一本講編程的書從頭讀到尾……編程不是考試,它是基于現實生活的創造。這種創造是漸進的,你在創造之初可能也無法預料到結果會是如何,這是任何創造性活動的基本屬性。
我推薦的第一本書是《 計算機程序的構造和解釋 》,英文名是《 Structure and Interpretation of Computer Programs 》,簡稱 SICP 。英語閱讀能力好的同學,可以看英文版。中文閱讀能力好的同學,可以看中文譯本,裘宗燕老師的文字素養與翻譯的嚴謹程度是可贊的。這本書的閱讀,建議分以下三個階段:
-
閱讀前兩章,第一章是講計算過程的抽象方法,第二章是將基本的數據的抽象方法。這兩章的內容涵蓋了軟件世界的『九年制義務教育』的全部內容,所用的教學語言也是非常成熟且設計精巧的 Scheme 語言的一個很小的子集。學習這兩章內容的過程中,可以穿插著閱讀《 Teach Yourself Scheme in Fixnum Days 》的前 10 章,這份 Scheme 教程也有一份 中文譯本 。SICP 的習題,即使不去做,也應該把題目看一下,動腦子想一想,判斷一下能不能做得出來。這些習題,在網絡上很容易找到答案。
-
復習 C 語言,教材用 Kernighan 與 C 語言之父 Ritchie 合寫的那本《 C 程序設計語言 》即可。這個階段的設置,主要是面向我們實驗室內部。因為我們實驗室里的同學在本科階段通常是要修 C 語言這門課的,但是當時他們可能并未真正從學習編程的角度去學習,現在可以通過第一階段基于 Scheme 語言建立的編程觀念去重新認識一下 C 語言,只有這樣方能理解 C 語言的優點與缺點,并且去思考如何充分發揮 C 的優勢,然后用 Scheme 來彌補 C 的不足。借助 GNU Guile 2,很容易實現 C 與 Scheme 復合編程。這個過程可以穿插閱讀 Kernighan 寫的《 程序設計實踐 》。
-
閱讀 SICP 的第 3 章,然后再找一本講 C++ 的書,比如 C++ 世界中非常有名但我不以為然的磚書《 C++ Primer 》, 只學習基于類的數據抽象以及面向對象編程部分即可 。這個階段,SICP 的第三章闡述了面向對象編程與函數式編程兩種方法。從 SICP 中獲得的面向對象編程,可以在 C++ 的學習中得到進一步一些強化,至于函數式編程方法,可以假裝自己已經知悉,留待日后需要時再作打算。最后,記得將《 Teach Yourself Scheme in Fixnum Days 》剩下的內容看完。
對于大部分編程任務而言,上述書所涉及的知識已經足夠用了,而且上面的這幾本書也是非常耐讀的書,只要你不是那么著急的將它們讀完,它們總是很有趣。我很喜歡 SICP 與《程序設計實踐》這兩本書,因為太喜歡了,所以一直都不舍得把它們讀完。
讓實踐有些難度
書是要看的,但是看書的過程中最好開動你的雙手。所以,你不應該停下來問自己:為何要學習編程?
我希望總有一天,你能給自己找到一個答案,那就是你想寫一個 XXX 程序。這個程序至少應該對你是有用,亦即它的主要功能不與你的系統里的其他程序存在著重復。如果你能明確這一點,那么你所創造的程序就有了意義,你的學習就有了意義。
為何要學習編程?因為你要創造一些從來沒有的軟件,而且它能夠幫助你做一些你認為是很重要的事!凡是你認為重要的事,對于很多人而言,很有可能也是很重要的,因此你所創造的東西就可以幫助更多的人,這意味著會有一些你可能不認識的人需要你,這就是你的價值所在。
編程的實踐,應該將它作為探索未知世界的智力活動,應該從書中跳出來,將自己從那些示例中獲得的經驗用于解決現實中的問題。如果你覺得,現實中根本不存在什么問題需要你去解決。那么……請你回答一下『你為何而存在』這個問題吧。
看書,是從前人正確的經驗中學習。實踐,是從自己的失敗中學習。既然決定要實踐了,所以還是給自己找一些比較難走的路走走看吧。
問題是最重要的
假如你已經有了非常多次的實踐上的失敗,并且你已經大致掌握了 Scheme 與 C/C++ 這樣的語言,那么每年學一門新的語言,這并非難事。可能你會對網絡上經常發生的語言之戰覺得奇怪。
是問題決定了語言,是問題決定了編程范式,是問題決定了信仰。如果你能很明確的認識到這些,那么你就不會陷入某種語言宗教的泥淖之中。對于許多事都是如此……搞機械的人,也經常信仰 UGNX,CATIA,PROE 這些『宗教』的……
如果非要給自己找一種信仰,那么 我信仰我的存在就是為了解決問題的 。
如果在我用的 Linux 系統上做一些自動化程度高一些的維護任務,我不會厭憎佶屈聱牙的 Bash 腳本,而是非常欣賞它像膠水一樣快速的將幾個本來是獨立運行的程序連接起來替我完成復雜的任務。
如果我要臨時的做一些文本處理工作,我可以用 python 3,因為它對 UTF-8 支持的挺好,而且字符串庫功能齊備。如果只是進行一些文本的替換,emacs 或 sed 之類現成的工具也夠用了。
如果我要寫一個嚴肅的程序,嚴肅到了它的生命可能要很久,那我會選擇一門成熟穩定的語言來實現它,即使用 C,我也不會煩棄它的繁瑣的代碼,我會盡力凝練程序中要實現的功能。
人生中本來就面臨著許多選擇,但是非常多的人在選擇之前并未認真的去考察自己面對的問題。
算法
解決問題需要算法。既然編程無處不在,那么算法也是無處不在的。但是,如果隨便拿起一本講算法的書,隨便一本,可能都會讓你覺得頭昏腦脹。也許你會擔心,連算法的書都看不懂,還怎么寫程序?
當初我剛學習編程的時候,寫過二十四點、漢諾塔、八皇后、俄羅斯方塊之類的小程序。后來,在現實的項目里,也寫過堆排序、快速排序、矩陣的 LU 與 SVD 分解、圖的最小生成樹與最短路徑之類的程序。但是現在,隨便拿一個讓我去實現,我還是不得不去翻書看懂算法,然后再去寫程序……
我想說的是,如果你正在閱讀一本講算法的書,書里有些算法或它的示例是你一時無法看懂的,可以跳過去。很多專門講算法的書里,充斥著心智游戲。如果你無法將自己代入到這些游戲的情境中,這個游戲的玩法自然就是不明了的。現在看起來,這是很自然的事,然而當初我卻一遍又一遍的懷疑自己的智商,特別是看到網絡上很多人像喝白開水一樣的談論著這些心智游戲,我一度懷疑,我不適合做編程方面的事。
幸好,這個世界足夠穩定,以至于我們不需要了解相對論與量子力學也能夠很好的生活下去。大部分人,連牛頓力學都不需要了解……算法也是這樣,特別是現在已經存在了它的實現,例如幾乎任何一種編程語言的標準庫中都提供一維數據的快速排序算法的實現。基本上,只要是對現實中的問題非常重要的算法,你總是能夠找到它們的既有實現,取而用之。
我不是說學習算法沒有必要,我只是強調不要被一時難以理解的算法擋住你。你天生就擁有一些無比強大的算法,它們是窮舉、貪婪與分治,還有最強大的『演化』與『神經網絡』。那些專門講算法的書,只不過是是了很蹩腳的語言、符號以及示例將你天生的直覺刻畫出來而已。只要你在現實中遇到問題,你總是能夠找到求解這個問題的方法,而不是只有讀懂了某本講算法的書你才能解決這個問題。
很多算法書,都是我看不懂的。它們的第一章就是讓我復習數學歸納法,第二章就是讓我學習算法的時間與空間復雜度分析……而我屬于對數學缺乏直覺的人,對我而言,這些書的唯一價值就是故意不讓我去讀它。即使是我心目中的大神 Knuth 的傳世之作《計算機編程藝術》,它唯一的目的似乎就是讓我覺得我不是搞藝術的。
很久之后,我在學校圖書館閑逛的時候,發現了《 如何求解問題:現代啟發式方法 》這本書,翻了翻,就開始嘆息,為什么一開始不知道這本書?
增強對計算機的理解
我們這些非計算機科班出身的人,有時間可以閱讀一些專業性強一些的計算機理論的書籍,譬如操作系統原理、編譯原理、算法與數據結構之類。看不懂太專業的書,或者沒那么多時間和精力,可以看看計算機科學的一些科普著作。有本《 通靈芯片 》值得一看,薄薄的小冊子,三五天的業余時間就可以看完。有本《 編碼:隱匿在計算機軟硬件背后的語言 》,算是《通靈芯片》的加強版,也值得一看。有一本《 深入理解計算機系統 》,以程序員的視角來看計算機的軟硬件系統,也是一本很好的書,不過就是要讀完它,需要一些耐心與時間,所以沒必要一次性看完。也可以繼續將 SICP 的第四、五章看完。
雖然你的編程技能不會因為讀了這些講述計算機原理的書而突飛猛進,但是這些書可以讓你理解你的程序是在一個什么樣的世界里運行的。雖然你不知道自己為何而存在,但是你知道這個程序為何而存在。你不僅知道它為何而存在,還知道它怎樣存在,并且也知道怎樣讓它更好的存在。這樣,也就沒必要在那些所謂的『XXX 箴言』、『XXX 之道』、『XXX 之禪』的書籍上浪費你有限的生命。
有時間,也可以復習一下《 黑客帝國 》,它的導演雖然不是程序猿,但勝似程序猿。看完黑客帝國,也可以看看 Steven Levy 寫的《 黑客:計算機革命的英雄 》。從技術層面躍遷到人文層面,也許那時你會對自己的人生有著更為深刻的認識。
對生命進行一些思考
下面是我的一些思考……我預先接受各種嘲笑。
我覺得,如果每個人都思考過『我們為何而存在』這個問題,那么就意味著對于這個問題的思考,我們不是自發的,而是被迫的。這個問題,自我們誕生以來就像烙印一樣存在于每個人的思想里。這個問題是人類的 終極問題 ,我們所解決的一切問題最終都會指向它。
我們為這個問題而生。不僅僅是為了自己去回答這個問題而生,還要為他人回答這個問題而創造生存條件。他人,包括我們的家人,也包括我們的朋友以及那些我們并不不認識的人。因為,這個問題是人類思想中的烙印,而我們是人類的一部分。
我們努力賺錢,是為了過更好的生活么?似乎并非如此,生活條件的富足,僅僅是為了我們去思考『我們為何而存在』而創造一個更好的環境,但是即使是世界上最窮的人,也會自問,我為何而存在,上蒼為何如此待我之類的問題。在這個問題面前,富人與窮人是絕對平等的。
我們看見美女,可能會怦然心動,追而娶之,是為了一段美好的愛情么?也許美色是一種誘惑,用于保證人類能夠繼續繁衍生息,以便繼續思考那個終極問題;即使是出于愛情,也極有可能是因為你愛的人對于你回答『我為何而存在』這個問題具有增益作用。
一切的『生』物,皆為『命』而生,這就是生命。生,是一種可變并且可自我繁衍的狀態。只要有一種事物,它的狀態是可變的,而且這種狀態在保持自身變化的過程中能夠產生新的可變且可繁衍的狀態,那么它就有『生』。那么『命』是什么?漢字的『命』,有點意思,它有點『一個人一生都在叩問』的意思。
我們在叩問什么……自然是『我們為何而存在』!?
從其他領域尋找答案
如果你像我這樣開始有點走火入魔了,那么為了尋找答案,有必要閱讀一些哲學、物理、生物之類的書籍。
也許你該看點哲學類的書。如果從未想過去看哲學的書,我推薦一本 14 歲的少男少女就應該閱讀的《 蘇菲的世界 》,你不要打我。羅素的那本《 西方哲學史 》雖然出自他個人的視角,但是顯然我們對哲學的理解也很難達到他那樣的高度,鑒于我們也不是打算去在哲學上有所成就,《西方哲學史》足夠我們看的了。
復習物理。《 費恩曼物理學講義 》第一卷就很好,人類所能感知到的這個世界,費恩曼像講故事一樣的差不多一網打盡了。如果連費曼的書都看不懂,不妨看看《 時間的形狀 》+《 量子物理史話 》,它們是近年來在相對論及其之后的物理學方面中國人寫的非常優秀的科普書。還有一本是我大學時經常看的《 從一到無窮大 》,雖然年代已頗為久遠,但依然不失為極好的物理科普著作。
生物學,這門課在中學時是我最討厭的課程之一。因為我實在是看不懂書里的插圖,鄉村中學連個顯微鏡與真實的標本都沒有,所以長期以來,我一直都是個生物盲。很多常見的花草樹木鳥獸蟲魚,我連它們的名字都叫不上來,更不要說它們具體屬于哪個門綱目科屬種了。但是有一本生物學的書我還是能看懂的——《 漫畫玩轉遺傳學 》,這是本非常好的遺傳學科普書。當時我是因為學習遺傳算法而買來的,結果從它從第一頁開始就把我的眼睛抓住了。
但是,很誠懇的說,這些書都讀完,我還是不知道『我為何而存在』這個問題的答案,但是我對編程有了更多的思考。特別是最近,量子力學將基本粒子的運動歸結為概率問題的思想,這對于我一直致力去解決的一個問題有很大的啟發。
這些書你都看過么?
都看過,但是有一些沒有看完。有些書是看完了,但是時間久了,有些遺忘,一直想找點時間再重讀一遍。
有些書是因為實在太好,不忍心一下都看完。這樣說,有些矯情,但事實就是如此。像《費恩曼物理學講義》,雖然有著物理學界小飛俠之稱的費恩曼已經將深邃的物理學變成了我能夠讀懂的人類語言,但是一方面我不是專業研究物理學的人,我沒有必要趕進度似的將他的書徹底讀完,另一方面是一旦讀完了,我就會惋惜,它不會再有第二季……我打算將費恩曼的這三卷物理學講義留給我今后的時間里慢慢的去看,我也可以把這些知識講給我的兒子聽。
有些書對我而言是因為太難,即使我覺得已經看完了,但過一段時間發現,跟沒看過是一樣的。這些書,我也只能歸類為沒有看完的書。
凡是我看的書,都是我認為在書中所涉及的方向上,作者比我走的更為深遠。但是我從不認為我讀不懂他們的書,就無法比他們走的再遠一些。因為這些書的作者,也總有一些書是他們讀不懂的。