剛上初中時(shí)我便開始了編程學(xué)習(xí),很不幸,我讀完了好幾本當(dāng)時(shí)普遍存在的諸如《21天精通C++》這類的垃圾書,當(dāng)時(shí)讀完也無(wú)大礙,甚至還能寫點(diǎn)小程序。但是軟件出故障了我不知道為什么,稍顯龐大的編程問(wèn)題無(wú)從下手,碰到現(xiàn)有的庫(kù)做不到的事也只能兩手一攤。雖然我每天不停地編碼,但我發(fā)現(xiàn)自己的編程能力卻是提高的如此緩慢,對(duì)于「迭代」與「遞歸」的概念只有極其有限的了解,可以說(shuō)只是把計(jì)算機(jī)當(dāng)成了計(jì)算器來(lái)使用。
進(jìn)入大學(xué)后,我主修了物理學(xué),最初的一段時(shí)間里我一直在記憶背誦那些物理公式,卻不理解她們是如何得出的,她們之間有什么聯(lián)系,亦或是她們的意義。我不停地學(xué)習(xí)如何計(jì)算解答一些常見的物理問(wèn)題,卻對(duì)在這些Hows背后的Whys一無(wú)所知。
而在我嘗試做一些基于物理行為的電腦游戲時(shí)我再次遇到了之前的的困難:面對(duì)新問(wèn)題時(shí)無(wú)從下手,面對(duì)新問(wèn)題時(shí)的恐懼不斷累積滋生,我開始主動(dòng)逃避,不去真正地理解,而是幻想能通過(guò)Google搜索復(fù)制粘貼代碼解決問(wèn)題。幸運(yùn)的是,大二時(shí)的一堂課完全改變了我的學(xué)習(xí)方法。那是第一次我有了「開天眼」的感覺,我痛苦地意識(shí)到,我對(duì)一些學(xué)科只有少的可憐的真正的理解,包括我主修的物理與輔修的計(jì)算機(jī)科學(xué)。
關(guān)于那堂課:那時(shí)我們剛剛學(xué)習(xí)完電學(xué)和狹義相對(duì)論的內(nèi)容,教授在黑板上寫下了這兩個(gè)主題,并畫了一根線將他們連了起來(lái)?!讣僭O(shè)我們有一個(gè)電子沿導(dǎo)線以相對(duì)論級(jí)別的速度移動(dòng)…」,一開始教授只是寫下了那些我們所熟悉的電學(xué)與狹義相對(duì)論的常見公式,但在數(shù)個(gè)黑板的代數(shù)推導(dǎo)后,磁場(chǎng)的公式神奇的出現(xiàn)了。雖然幾年前我早已知道這個(gè)公式,但那時(shí)我根本不知道這些現(xiàn)象間的有著這樣潛在的聯(lián)系。磁與電之間的差別只是「觀察角度」的問(wèn)題,我猛然醒悟,此后我不再僅僅追求怎么做(How),我開始問(wèn)為什么(why),開始回過(guò)頭來(lái),拾起那些最基礎(chǔ)的部分,學(xué)習(xí)那些我之前我本該好好學(xué)的知識(shí)。這個(gè)回頭的過(guò)程是痛苦的,希望你們能就此警醒,永遠(yuǎn)不要做這種傻事。
這幅圖取自 Douglas Hofstadter 的著作G?del, Escher, Bach。圖中的每一個(gè)字母都由其他更小的字母組成。在最高層級(jí),我們看的是"MU",M 這個(gè)字母由三個(gè)HOLISM(整全觀)構(gòu)成,U則是由一個(gè)REDUCTIONISM(還原論)構(gòu)成,前者的每一個(gè)字母都包含后者的后者整個(gè)詞,反之亦然。而在最低層級(jí),你會(huì)發(fā)現(xiàn)最小的字母又是由重復(fù)的"MU"組成的。
每一層次的抽象都蘊(yùn)含著信息,如果你只是幼稚地單一運(yùn)用整體論在最高層級(jí)觀察,或運(yùn)用還原論觀察最低層級(jí),你所得到的只有"MU"(在一些地區(qū)的方言中mu意味著什么都沒有)。問(wèn)題來(lái)了,怎樣才能盡可能多的獲取每個(gè)層級(jí)的信息?或者換句話說(shuō),該怎樣學(xué)習(xí)復(fù)雜領(lǐng)域(諸如編程)包含的眾多知識(shí)?
教育與學(xué)習(xí)過(guò)程中普遍存在一個(gè)關(guān)鍵問(wèn)題:初學(xué)者們的目標(biāo)經(jīng)常過(guò)于傾向整全觀而忽略了基礎(chǔ),舉個(gè)常見的例子,學(xué)生們非常想做一個(gè)機(jī)器人,卻對(duì)背后的
理解物理模型 → 理解電子工程基礎(chǔ) → 理解伺服系統(tǒng)與傳感器 → 讓機(jī)器人動(dòng)起來(lái)
這一過(guò)程完全提不起興趣。
在這里對(duì)于初學(xué)者有兩個(gè)大坑:
如果初學(xué)者們只與預(yù)先構(gòu)建好的「發(fā)動(dòng)機(jī)和組件」接觸(沒有理解和思考它們構(gòu)造的原理),這會(huì)嚴(yán)重限制他們?cè)趯?lái)構(gòu)建這些東西的能力,并且在診斷解決問(wèn)題時(shí)無(wú)從下手。
但也不能矯枉過(guò)正,陷入還原論的大坑,初學(xué)時(shí)便一心試圖做宏大的理論,這樣不僅有一切流于理論的危險(xiǎn),枯燥和乏味還會(huì)讓你失去推動(dòng)力。這種情況經(jīng)常發(fā)生在計(jì)算機(jī)科班生身上。
為了更好理解,可以將學(xué)習(xí)編程類比為學(xué)習(xí)廚藝:你為了燒得一手好菜買了一些關(guān)于菜譜的書,如果你只是想為家人做菜,這會(huì)是一個(gè)不錯(cuò)的主意,你重復(fù)菜譜上的步驟也能做出不賴的菜肴,但是如果你有更大的野心,真的想在朋友面前露一手,做一些獨(dú)一無(wú)二的美味佳肴,甚至成為「大廚」,你必須理解這些菜譜背后大師的想法,理解其中的理論,而不僅僅是一味地實(shí)踐。但是如果你每天唯一的工作就是閱讀那些厚重的理論書籍,因?yàn)槿狈?shí)踐,你只會(huì)成為一個(gè)糟糕的廚子,甚至永遠(yuǎn)成為不了廚子,因?yàn)榭戳藥滋鞎竽憔鸵驗(yàn)榭菰锓艞壛藦N藝的學(xué)習(xí)。
總之,編程是連接理論與實(shí)踐的紐帶,是計(jì)算機(jī)科學(xué)與計(jì)算機(jī)應(yīng)用技術(shù)相交融的領(lǐng)域。正確的編程學(xué)習(xí)方法應(yīng)該是:通過(guò)自頂而下的探索與項(xiàng)目實(shí)踐,獲得編程直覺與推動(dòng)力;從自底向上的打基礎(chǔ)過(guò)程中,獲得最重要的通用方法并鞏固編程思想的理解。
作為初學(xué)者,應(yīng)以后者為主,前者為輔。
「學(xué)編程應(yīng)該學(xué)哪門語(yǔ)言?」這經(jīng)常是初學(xué)者問(wèn)的第一個(gè)問(wèn)題,但這是一個(gè)錯(cuò)誤的問(wèn)題,你最先考慮的問(wèn)題應(yīng)該是「哪些東西構(gòu)成了編程學(xué)習(xí)的基礎(chǔ)」?
編程知識(shí)的金字塔底部有三個(gè)關(guān)鍵的部分:
算法思想:例如怎樣找出一組數(shù)中最大的那個(gè)數(shù)?首先你得有一個(gè) maxSoFar 變量,之后對(duì)于每個(gè)數(shù)…
語(yǔ)法:我怎樣用某種編程語(yǔ)言表達(dá)這些算法,讓計(jì)算機(jī)能夠理解。
啟蒙階段的初學(xué)者若選擇C語(yǔ)言作為第一門語(yǔ)言會(huì)很困難并且枯燥,這是因?yàn)樗麄儽黄纫瑫r(shí)學(xué)習(xí)這三個(gè)部分,在能做出東西前要花費(fèi)很多時(shí)間。
因此,為了盡量最小化「語(yǔ)法」與「系統(tǒng)基礎(chǔ)」這兩部分,建議使用 Python 作為學(xué)習(xí)的第一門語(yǔ)言,雖然Python對(duì)初學(xué)者很友好,但這并不意味著它只是一個(gè)「玩具」,在大型項(xiàng)目中你也能見到它強(qiáng)大而靈活的身影。熟悉Python后,學(xué)習(xí)C語(yǔ)言是便是一個(gè)不錯(cuò)的選擇了:學(xué)習(xí)C語(yǔ)言會(huì)幫助你以靠近底層的視角思考問(wèn)題,并且在后期幫助你理解操作系統(tǒng)層級(jí)的一些原理,如果你只想成為一個(gè)普通(平庸)的開發(fā)者你可以不學(xué)習(xí)它。
下面給出了一個(gè)可供參考的啟蒙階段導(dǎo)引,完成后你會(huì)在頭腦中構(gòu)建起一個(gè)整體框架,幫助你進(jìn)行自頂向下的探索。
閱讀《編碼的奧秘》
PS:如果教育對(duì)象還是一個(gè)孩子,以下的資源會(huì)很有幫助(年齡供參考):
5-8歲:?Turtle Academy
8-12歲:Python for Kids
結(jié)束啟蒙階段后,初學(xué)者積累了一定的代碼量,對(duì)編程也有了一定的了解。這時(shí)你可能想去學(xué)一門具體的技術(shù),諸如Web開發(fā),Android開發(fā),iOS開發(fā)什么的,你可以去嘗試做一些盡可能簡(jiǎn)單的東西,給自己一些正反饋,補(bǔ)充自己的推動(dòng)力。但記住別深入,這些技術(shù)有無(wú)數(shù)的細(xì)節(jié),將來(lái)會(huì)有時(shí)間去學(xué)習(xí);同樣的,這時(shí)候也別過(guò)于深入特定的框架和語(yǔ)言,現(xiàn)在是學(xué)習(xí)計(jì)算機(jī)科學(xué)通用基礎(chǔ)知識(shí)的時(shí)候,不要試圖去抄近路直接學(xué)你現(xiàn)在想學(xué)的東西,這是注定會(huì)失敗的。
那么入門階段具體該做些什么呢?這時(shí)候你需要做的是反思自己曾經(jīng)寫過(guò)的程序,去思考程序?yàn)槭裁?Why)要這樣設(shè)計(jì)?,思考怎樣(How)寫出更好的程序?試圖去探尋理解編程的本質(zhì):利用計(jì)算機(jī)解決問(wèn)題。
設(shè)想 :
X = 用于思考解決方案的時(shí)間,即「解決問(wèn)題」 部分
Y = 用于實(shí)現(xiàn)代碼的時(shí)間,即「利用計(jì)算機(jī)」部分」
編程能力 = F(X, Y) (X>Y)
要想提高編程能力,就得優(yōu)化 X,Y 與函數(shù) F(X, Y),很少有書的內(nèi)容能同時(shí)著重集中在這三點(diǎn)上,但有一本書做到了——Structure and Interpretation of Computer Programs(SICP)《計(jì)算機(jī)程序的構(gòu)造和解釋》,它為你指明了這三個(gè)變量的方向。在閱讀SICP之前,你也許能通過(guò)調(diào)用幾個(gè)函數(shù)解決一個(gè)簡(jiǎn)單問(wèn)題。但閱讀完SICP之后,你會(huì)學(xué)會(huì)如何將問(wèn)題抽象并且分解,從而處理更復(fù)雜更龐大的問(wèn)題,這是編程能力巨大的飛躍,這會(huì)在本質(zhì)上改變你思考問(wèn)題以及用代碼解決問(wèn)題的方式。此外,SICP的教學(xué)語(yǔ)言為 Scheme,可以讓你初步了解函數(shù)式編程。更重要的是,他的語(yǔ)法十分簡(jiǎn)單,你可以很快學(xué)會(huì)它,從而把更多的時(shí)間用于學(xué)習(xí)書中的編程思想以及復(fù)雜問(wèn)題的解決之道上。
Peter Norvig?曾經(jīng)寫過(guò)一篇非常精彩的SICP書評(píng),其中有這樣一段:
To use an analogy, if SICP were about automobiles, it would be for the person who wants to know how cars work, how they are built, and how one might design fuel-efficient, safe, reliable vehicles for the 21st century. The people who hate SICP are the ones who just want to know how to drive their car on the highway, just like everyone else.
如果你是文中的前者,閱讀SICP將成為你銜接啟蒙與入門階段的關(guān)鍵點(diǎn)
雖然SICP是一本「入門書」,但對(duì)于初學(xué)者還是有一定的難度,以下是一些十分有用的輔助資源:
完成了這部分學(xué)習(xí)后,你會(huì)逐步建立起一個(gè)自己的程序設(shè)計(jì)模型,你的腦子里不再是一團(tuán)亂麻,你會(huì)意識(shí)到記住庫(kù)和語(yǔ)法并不會(huì)教你如何解決編程問(wèn)題,接下來(lái)要學(xué)些什么,在你心里也會(huì)明朗了很多。這時(shí)候才是真正開始進(jìn)行項(xiàng)目實(shí)踐,補(bǔ)充推動(dòng)力的好時(shí)機(jī)。關(guān)于項(xiàng)目實(shí)踐:對(duì)于入門階段的初學(xué)者,參與開源項(xiàng)目還為時(shí)過(guò)早,這時(shí)候應(yīng)該開始一些簡(jiǎn)單的項(xiàng)目,諸如搭建一個(gè)網(wǎng)站并維護(hù)它,或是編寫一個(gè)小游戲再不斷進(jìn)行擴(kuò)展,如果你自己的想法不明確,可以從?Mega Project List?中選取項(xiàng)目。總之,務(wù)必在這時(shí)拿下你項(xiàng)目實(shí)踐的第一滴血。
如果你覺得SICP就是搞不定,也不要強(qiáng)迫自己,先跳過(guò),繼續(xù)走常規(guī)路線:開始讀The Elements of Computing Systems?吧,它會(huì)教會(huì)你從最基本的 Nand 門開始構(gòu)建計(jì)算機(jī),直到俄羅斯方塊在你的計(jì)算機(jī)上順利運(yùn)行。?具體內(nèi)容不多說(shuō)了,這本書會(huì)貫穿你的整個(gè)編程入門階段,你入門階段的目標(biāo)就是堅(jiān)持完成這本書的所有項(xiàng)目(包括一個(gè)最簡(jiǎn)的編譯器與操作系統(tǒng))。
為了完全搞定這本書,為了繼續(xù)打好根基。為了將來(lái)的厚積薄發(fā),在下面這幾個(gè)方面你還要做足功課(注意:下面的內(nèi)容沒有絕對(duì)意義上的先后順序):
有了之前程序設(shè)計(jì)的基礎(chǔ)后,想更加深入地把握計(jì)算機(jī)科學(xué)的脈絡(luò),不妨看看這本書:《深入理解計(jì)算機(jī)系統(tǒng)》?Computer Systems A Programmer's Perspective。這里點(diǎn)名批評(píng)這本書的中譯名,其實(shí)根本談不上什么深入啦,這本書只是?CMU的「計(jì)算機(jī)系統(tǒng)導(dǎo)論」的教材而已。CMU的計(jì)算機(jī)科學(xué)專業(yè)相對(duì)較偏軟件,該書就是從一個(gè)程序員的視角觀察計(jì)算機(jī)系統(tǒng),以「程序在計(jì)算機(jī)中如何執(zhí)行」為主線,全面闡述計(jì)算機(jī)系統(tǒng)內(nèi)部實(shí)現(xiàn)的諸多細(xì)節(jié)。
如果你看書覺得有些枯燥的話,可以跟一門 Coursera 上的 MOOC:?The Hardware/Software Interface,這門課的內(nèi)容是 CSAPP 的一個(gè)子集,但是最經(jīng)典的實(shí)驗(yàn)部分都移植過(guò)來(lái)了。同時(shí),可以看看?The C Programming Language,回顧一下C語(yǔ)言的知識(shí)。
完成這本書后,你會(huì)具備堅(jiān)實(shí)的系統(tǒng)基礎(chǔ),也具有了學(xué)習(xí)操作系統(tǒng),編譯器,計(jì)算機(jī)網(wǎng)絡(luò)等內(nèi)容的先決條件。當(dāng)學(xué)習(xí)更高級(jí)的系統(tǒng)內(nèi)容時(shí),翻閱一下此書的相應(yīng)章節(jié),同時(shí)編程實(shí)現(xiàn)其中的例子,一定會(huì)對(duì)書本上的理論具有更加感性的認(rèn)識(shí),真正做到經(jīng)手的代碼,從上層設(shè)計(jì)到底層實(shí)現(xiàn)都了然于胸,并能在腦中回放數(shù)據(jù)在網(wǎng)絡(luò)->內(nèi)存->緩存->CPU的流向。
此外,也是時(shí)候去接觸 UNIX 哲學(xué)了: KISS - Keep it Simple, Stupid. 在實(shí)踐中,這意味著你要開始熟悉命令行界面,配置文件。并且在開發(fā)中逐漸脫離之前使用的IDE,學(xué)會(huì)使用Vim或Emacs(或者最好兩者都去嘗試)。
閱讀 《UNIX編程環(huán)境?》
如今,很多人認(rèn)為編程(特別是做web開發(fā))的主要部分就是使用別人的代碼,能夠用清晰簡(jiǎn)明的方式表達(dá)自己的想法比掌握硬核的數(shù)學(xué)與算法技巧重要的多,數(shù)據(jù)結(jié)構(gòu)排序函數(shù)二分搜索這不都內(nèi)置了嗎?工作中永遠(yuǎn)用不到,學(xué)算法有啥用啊?這種扛著實(shí)用主義大旗的「碼農(nóng)」思想當(dāng)然不可取。沒有扎實(shí)的理論背景,遭遇瓶頸是遲早的事。
數(shù)據(jù)結(jié)構(gòu)和算法是配套的,入門階段你應(yīng)該掌握的主要內(nèi)容應(yīng)該是:這個(gè)問(wèn)題用什么算法和數(shù)據(jù)結(jié)構(gòu)能更快解決。這就要求你對(duì)常見的數(shù)據(jù)結(jié)構(gòu)和算法了熟于心,你不一定要敲代碼,用紙手寫流程是更快的方式。對(duì)你不懂的數(shù)據(jù)結(jié)構(gòu)和算法,你要去搜它主要拿來(lái)干嘛的,使用場(chǎng)景是什么。
供你參考的學(xué)習(xí)資源:
Different languages solve the same problems in different ways. By learning several different approaches, you can help broaden your thinking and avoid getting stuck in a rut. Additionally, learning many languages is far easier now, thanks to the wealth of freely available software on the Internet
此外還要知道,學(xué)習(xí)第n門編程語(yǔ)言的難度是第(n-1)門的一半,所以盡量去嘗試不同的編程語(yǔ)言與編程范式,若你跟尋了前文的指引,你已經(jīng)接觸了:「干凈」的腳本語(yǔ)言 Python, 傳統(tǒng)的命令式語(yǔ)言 C, 以及浪漫的函數(shù)式語(yǔ)言 Scheme/Racket 三個(gè)好朋友。但僅僅是接觸遠(yuǎn)遠(yuǎn)不夠,你還需要不斷繼續(xù)加深與他們的友誼,并嘗試結(jié)交新朋友,美而雅的?Ruby?小姑娘,Hindley-Milner 語(yǔ)言家族的掌中寶?Haskell?都是不錯(cuò)的選擇。但有這么一位你躲不開的,必須得認(rèn)識(shí)的大伙伴 — C++,你得做好與他深交的準(zhǔn)備:
[可選] 進(jìn)階:
現(xiàn)實(shí)是殘酷的,在軟件工程領(lǐng)域仍舊充斥著一些狂熱者,他們只掌握著一種編程語(yǔ)言,也只想掌握一種語(yǔ)言,他們認(rèn)為自己掌握的這門語(yǔ)言是最好的,其他異端都是傻X。這種人也不是無(wú)藥可救,有一種很簡(jiǎn)單的治療方法:讓他們寫一個(gè)編譯器。要想真正理解編程語(yǔ)言,你必須親自實(shí)現(xiàn)一個(gè)。現(xiàn)在是入門階段,不要求你去上一門編譯器課程,但要求你能至少實(shí)現(xiàn)一個(gè)簡(jiǎn)單的解釋器。
供你參考的學(xué)習(xí)資源:
編程入門階段比較容易忽視的幾點(diǎn):
以上的內(nèi)容你不應(yīng)該感到懼怕,編程的入門不是幾個(gè)星期就能完成的小項(xiàng)目。期間你還會(huì)遇到無(wú)數(shù)的困難,當(dāng)你碰壁時(shí)試著嘗試「費(fèi)曼」技巧:將難點(diǎn)分而化之,切成小知識(shí)塊,再逐個(gè)對(duì)付,之后通過(guò)向別人清楚地解說(shuō)來(lái)檢驗(yàn)自己是否真的理解。當(dāng)然,依舊會(huì)有你解決不了的問(wèn)題,這時(shí)候不要強(qiáng)迫自己——很多時(shí)候當(dāng)你之后回過(guò)頭來(lái)再看這個(gè)問(wèn)題時(shí),一切豁然開朗。
此外不要局限于上文提到的那些材料,還有一些值得在入門階段以及將來(lái)的提升階段反復(fù)閱讀的書籍。這里不得不提到在?stackoverflow??上票選得出的程序員必讀書單中,排在前兩位的兩本書:
Code Complete?:不管是對(duì)于經(jīng)驗(yàn)豐富的程序員還是對(duì)于那些沒有受過(guò)太多的正規(guī)訓(xùn)練的新手程序員,此書都能用來(lái)填補(bǔ)自己的知識(shí)缺陷。對(duì)于入門階段的新手們,可以重點(diǎn)看看涉及變量名,測(cè)試,個(gè)人性格的章節(jié)。
The Pragmatic Programmer?:?程序員入門書,終極書。有人稱這本書為代碼小全:從?DRY?到?KISS,從做人到做程序員,這本書教給了你一切,你所需的只是遵循書上的指導(dǎo)。
這本書的作者?Dave?,在書中開篇留了這樣一段話:
You’re a Pragmatic Programmer. You aren’t wedded to any particular technology, but you have a broad enough background in the science, and your experience with practical projects allows you to choose good solutions in particular situations.Theory and practice combine to make you strong. You adjust your approach to suit the current circumstances and environment. And you do this continuously as the work progresses. Pragmatic Programmers get the job done, and do it well.
這段話以及他創(chuàng)立的?The Pragmatic Bookshelf?一直以來(lái)都積極地影響著我,因此這篇指南我也盡量貫徹了這個(gè)思想,引導(dǎo)并希望你們成為一名真正的 Pragmatic Programmer 。
更多建議: