現實的編程
來自專欄互聯網商業分析入門
下面的不是我寫的,還有我非常好奇為什麼學編程的觀點在人群中會兩極分化。我不覺得學編程是應該被認為是多麼難的事情。但是我不明白為什麼自己會這麼反感,我也不相信經過高考選拔出來的重點大學的學生智商會不夠用。我也不相信那些北大青鳥培訓出來的人就是天生擅長編程。所以我覺得一定是有些地方出了問題。我現在的理想非常簡單:在大學裡,把編程學精,然後找到學習編程的捷徑,讓人人都會編程。讓編程不再是神秘,高智商者獨享的存在。女生也可以把編程學得很好,學編程不再是有點獃滯或者難以用語言表述的東西,我討厭這樣的定義。所有人都能學會編程,做出一些他們需要的產品。這是我現在除了綜合績點4.0以外最大的夢想。
從大一入學被調劑到計算機專業,到喜歡上這個專業,再到畢業拿到10多個offer,最終進入理想的大廠工作。回想起來這些年確確實實踩了很多坑。我剛開始學習編程的時候也想一口吃成一個胖子,想速成,但是有時候卻是不盡人意。
回憶了下這幾年學習編程的過程,整理了一些我自己認為很需要注意的幾個方面,分享給大家。希望能讓初學編程的你,少走一些彎路,可能文章比較長,但我真心希望初學編程的你能夠認真看完,至少,我認為如果我剛學編程的時候看到這篇文章,對我或多或少是有一些幫助的。
我個人是一名計算機專業的學生,很多人可能會認為我是在課堂上學到的編程,其實不是這樣。
我認為科班出身和非科班出身的學生最大的區別在於科班出身的學生知道去學什麼,知道每一門課程是幹什麼的;還有一些必須完成的作業、小項目,促使他們去做一些實際的編碼練習,除此之外,真的全靠自學。
對於自學編程,我認為首先應該談的是如何去避免一些坑,這樣就可能節約大把的時間。下面我就以問題的形式來分享一些我認為重要的方面。
1. 我應該選擇什麼編程語言
可能困擾編程新手最多的一個問題是【我應該學什麼編程語言】或者【我需要學習哪些課程才能做出一個web、一個app】,很多人一直糾結這個問題,陷入了東學一點、西看一點的死循環,到頭來啥也沒學好,這會很浪費時間。
剛上大一的時候,我也很想知道應該選擇什麼編程語言。我問了很多人,網上各種查資料,但所能得到的答案都很片面,多數對這個問題答非所問,總是回答說「某某編程語言難」,「某某編程語言性能好」。其實作為初學者,我們對計算機體系都不了解,就不要過多地去糾結性能,或者難易等因素,原因我等下再說。
如果你有明確的方向,那麼很好選擇。如果你想做演算法、機器學習方向,那麼python是最好的選擇。如果你想做web開發,java、php等都可以。如果想做一些更底層的工作,那麼就可以選c。當然這是建立在你有明確方向的基礎上。可是,很多人都沒怎麼接觸過計算機行業,特別是和我一樣剛入學就被調劑到計算機專業的人。對這些同學來說,各個編程語言就只是個名字,除了叫法不一樣,你根本不知道它們有什麼差別。所以索性不要糾結了,我替你選一個吧。
如果你是在校大學生,那麼你有大把連續的時間,就先學習c,然後再學c++。我個人是學c入門的,也許很多人不理解我為什麼推薦學c,因為c和c++都很難、很複雜,看起來並不適合入門。然而正是它們的難和複雜才能讓你更好地理解計算機系統【計算機系統不是指操作系統】。學習編程不是學習編程語言,而是學習一個計算機生態,即一個龐大的知識體系。只會編程語言而不理解整個計算機的體系,就像只會寫字而寫不出好文章。了解c/c++和了解計算機系統是極為貼合的,向下可以幫助你更容易地理解操作系統、編譯原理、計算機網路、計算機組成原理,為什麼呢?因為較為底層的東西很多都是用c實現的,和系統的貼合度極高,很多教材源碼甚至教程,在講述這些知識的時候都是用c或c++作為媒介。而向上,c++面向對象的機制,也可以做出一些應用,譬如五子棋遊戲等,也不會顯得那麼枯燥。花個小半年時間了解c和c++,之後你就會覺得看書、看資料可以輕鬆很多。
如果你是一個上班族,但是剛剛學習編程,可能學c和c++對你來說有些複雜和困難,因為學習它們確實是很需要時間。你們不像在校生那樣有大把的連續時間,而零碎的時間去學習一個比較複雜的東西效果不見得有那麼好,所以可以先學一些【更容易見效】的編程語言,從python入手吧,至少能快速做出一些小應用,不至於丟失了興趣,但是真的要入門編程又還得看看與計算機系統相關的書籍,這樣才能更深層次地去編程,譬如【深入理解計算機系統】這一本書可以讀很多遍,這本書把整個計算機系統給串起來了。
2.學習編程,我需要學習哪些課程?
我要學哪些課程?我為什麼要學習如高數、離散數學、線性代數、概率論等課程?
這個問題也是之前困擾了我很久的問題。不過我現在想通了,對於【高數、離散、線性代數、概率論】等課程,很好解釋,做演算法的同學肯定知道為啥要學習這些課程。機器學習中會大量用到上述提到的課程,所以會比較好理解。對在校生而言,學校開設的很多課程我們不知道為什麼要學,我們很疑惑,不知道學它有什麼用,這個時候我們就會很糾結,還會產生抵觸情緒。這很正常,因為我們學習得不夠深入,自然不能理解它們的用處。
在我看來,大學本科課程更多的是面向「面」的教學,即什麼課程都教給你一些,但是又講得不那麼深入;而工作或者讀研,更多的則是面向「點」的學習,用到的知識更專。本科時,學校也不知道你以後是去搞演算法、還是搞架構、還是搞伺服器開發,甚至去搞硬體,所以學校需要你學很多課程,至少有個了解。對學生來說,一方面可以從中選擇自己感興趣的點;一方面也可以對未來的就業方向有些啟發。所以即使像數電、模電等課程,雖然之後可能用不著,但是你也要學,並且會花費大量的時間。雖然你最後不一定去搞硬體,但是這些課程也會讓你更容易去理解一些知識,比如cpu中的邏輯器件。
如果你在大一的時候就有一個明確的定位,知道自己今後想從事哪方面的工作,課程與課程之間是可以調一下優先順序的。不過像大學物理,這種課程確實是對編程沒有幫助,但是像我前面所說的,大學教育更注重廣度,大物等課程可能就是為了給你普及生活常識吧。
其實,大學教育的問題是普遍存在的,我認為我們學習一項技能的時候,應該採取的是項目驅動式學習,即需要用到什麼東西時不會了再去學,而不是先填鴨式的都填進腦子,並且在學習的過程中我們還不知道它這是幹嘛用的,等之後用到了,甚至不記得自己學過,反而查資料才會想起:哦,原來我之前學的xx科目是這個用處啊,可是我當時並沒有好好學。很多時候學生時間的浪費可能還是要怪老師、怪學校,他們一開始沒給我們做好充分的課程介紹。所以,在經過比較多的編程和項目實踐後,我認為一個比較好的學習方式是,改良版的項目驅動學習法。即:
學習一段時間,做個小項目,將做項目遇到的問題記下來,針對性地學習相關知識,然後再實踐,再學一段時間理論,讓知識成網狀發射狀地變大。當然,項目驅動式學習有一個弊端,就是每次學習的知識都是項目所需要的,很零碎、不成體系,所以需要改良,即在採取項目驅動學習法的時候每天抽一段時間去完整地讀一本書,或者一個相關問題的完整介紹,這樣就很容易把一些知識成體系地串起來。這樣一段時間下來,慢慢的,你就知道我們為什麼要學那麼多科目,學這些科目能幹什麼。
為了表達地更加形象,我就舉一個小例子,是我最近遇到的。我本身的工作是做Linux C++的,但不僅限於此。我個人對python、數據分析,以及機器學習等內容比較感興趣,大家可以看到我最近也在我的專欄發布了很多文章。就從數據獲取開始,我講講我這兩個月做了什麼東西。
談到數據獲取,可能最容易想到的是爬蟲,爬蟲是一個在知乎上被說爛了的話題,所以我不想多說它是什麼。很多時候有人覺得爬蟲簡單,為什麼呢,因為有現成的框架,所以獲取少量的數據就比較容易。但是當你需要爬取的數據很大的時候(比如我之前抓取了知乎500萬用戶的數據,在下班的時間、用自己家裡普通的pc,計算機性能並不是那麼好,比不上伺服器,又要在不被封IP的情況下抓到這麼大量的數據,然後對數據進行清洗,最後還要可視化展示),使用現成的爬蟲框架就並不是那麼容易實現了。況且,我需要抓很多數據源,並不是一鎚子買賣。所以我選擇去開發一個系統,即在現有的框架下進行二次開發,搭建一個屬於自己的爬蟲系統,並植入一些演算法。我在系統中添加了很多中間件,直到現在,它還可以在10分鐘內就部署一個能抓取大量數據的爬蟲應用。當然,這個過程也遇到了不少麻煩,我就簡單講講,怎麼去攻克一個個問題。
下面先給出一個樹形圖,從上往下每一個圈都代表了學習過程中遇到的難點,如果你現在看不懂,沒關係,我想告訴你的是一種梳理知識的方法:
如上圖所示,就是一個項目驅動式學習的例子,我們的目的是為了獲取數據,所以選擇了爬蟲:
- 爬蟲可以理解為一個簡單的過程:發送request,獲取response,然後提取數據。這個過程會涉及到網路,是發送http還是https請求;目標網站是否需要登錄,是post請求還是get請求,從這條線,衍生出了一條對網路進行學習的路徑。
- 獲取到網頁之後,如果不是結構化的數據,可能返回的是一個html源代碼,那麼可能就需要了解dom,或者html頁面解析的知識,甚至需要了解一下前端開發。
- 在抓取的過程中,經常會遇到數據中途不能被爬取的情況,一般是IP被封禁了,那麼可能又要用上代理,代理是什麼呢?http,https代理能不能混用呢?如何構建一個代理池呢?這裡又有很多要學習的東西。還有可能遇到的情況是,抓下來的數據是加密的,需要通過js解密,這時候就要了解一下js,如何用爬蟲模擬瀏覽器進行抓取。除此之外,如果抓取的頻率不對,很多數據源會給你假數據,這就是一些經驗問題了,本文不是技術文,所以就不多討論。
- 當解決了上述問題後,我們好像可以拿到一些數據了,但是當數據大起來,問題又複雜了,你可能需要使用分散式抓取了,這時候你可能需要了解一下redis,當request產生的速度大於其消費的速度之後,你的任務隊列可能爆炸,所以這裡又涉及到演算法和數據結構的應用了。
- 數據量上去之後,把數據寫在文件裡面是不靠譜的,這時候又涉及到存儲了,到底是使用關係型資料庫還是非關係型資料庫呢,有什麼區別呢?存進去的數據怎麼去重呢?為什麼insert操作越來越卡了呢?電腦怎麼越來越熱了呢?索引是什麼,什麼時候該建立索引呢?這裡又牽扯到資料庫原理相關的知識。
- 遇到一些比較難處理的網站,比如有驗證碼識別該怎麼辦呢?其實對於很多純數字和字母的驗證碼都很好解決,自己用深度學習訓練即可。在TensorFlow的Demo中就要生成驗證碼自己訓練的教程,然後制定個中間件放在爬蟲系統中,這個問題就解決了。可是什麼是深度學習呢?這裡又引出一條對深度學習進行探索的例子,而我自己也是之前在學校的時候自學了小半年機器學習,有了一定的基礎後,才能比較容易地上手TensorFlow框架。再往下就比較深了。
上述六點簡單講了講項目驅動式學習的介紹,其實,你看到的每一個小圓圈,深挖下去都大有文章。我們現在看到的只是冰山一角,任何一條學習路徑學習下去都深無止境,我們不可能完全學會,可是項目驅動式學習最大的好處是讓你知道你應該去學習什麼,而不是先學一大堆知識,再去做一個項目。嚴格來說,項目驅動式學習的可視化路徑是一張網,而不是一棵樹,這裡畫成樹狀只是為了便於大家理解。
除了獲得數據,還有清洗數據、分析數據,甚至挖掘數據,最後可視化數據並且展示數據,這裡我就不一一介紹了。可以參見下面這張圖,如果大家想看我做的一些成品,可以看看我的其他文章。
3.學習編程是否需要制定計劃?
學習編程是否需要制定計劃,該制定什麼樣的計劃呢?
我認為不只是編程需要制定計劃,其他任何的學習和工作都需要制定計劃。我從13年上大學就開始定期給自己制定計劃,這個習慣也一直堅持到了現在,受益匪淺。當然也不只是制定學習計劃,還可以列一些自己需要做的其他的事情。我最近在整理筆記的時候也發現了一些之前記錄的計劃和清單,可以給大家看看。比如下圖就是我14年寫的筆記,筆記上都留下了最後一次打開的時間。列舉了一些自己需要看的文章,因為當時不太懂得規劃,所以比較亂。
到了16年的時候,我做計劃做得更加有條理了。下圖是16年10月30日的計劃,那時候我已經大四了,並且已經找到了工作、簽了滿意的offer,並且沒有什麼課,按理說可以放鬆放鬆了,不過我還是制定了一些學習計劃,並且選擇在11月去百度實習。從內容上看,主要是學習英語和計算機專業課,因為大一大二的時候我確實不明白為什麼要學習專業課,到了大三下想清楚原因以後,我也就一直在重新學習,因為計算機專業課真的很重要!學好了這些課,能讓你在日後的學習工作中輕鬆不少:
除了大四制定的計劃外,大二的時候我也制定過較為詳細的學習計劃(如下圖),把需要學習的內容進行了編號,存入表格,這樣才能讓你過得有條不紊。當然,很難完全按照計划去執行,不過制定相應的計劃能讓你清楚地知道自己應該幹什麼。
所以,如果你是在校生,那麼好好制定一個計劃吧,因為你有大把的時間。當然,如果你已經畢業了,沒關係,我現在也在上班,同樣也列舉了自己最近要學習的內容,如下圖(2月27日更新過),包括了短期和長期需要學習的內容:
4.編程是否需要做筆記和寫博客?
我覺得,寫不寫博客無所謂,因為博客是要寫出來給大家看的,可能要保證格式美觀、語法也要盡量準確,最好比較有文采,我覺得太麻煩也就一直沒寫。而筆記是必須要做的,並且記筆記是一個長期的過程。在學習的過程中,我們一直都在追求一種最高效的學習方法,比如,同一個班的同學,他用他的學習方法考上了清華,而你用同樣的方法就不行,為什麼?因為他的方法對他自己而言是定製化的,可能且大概率不適合你,比如他的筆記你不一定能看懂,因為他可能設計了一套屬於自己的符號。而就編程而言,很多同學說善用搜索引擎,是對的,可是搜索引擎搜出來的是別人的答案。你照搬過來,也許可以用,但是你沒有記住,這些知識並不屬於你,之後你可能還會遇到同樣的問題,又要再搜索一遍,可能很難找到之前的那個答案了。但是記筆記就不一樣,記筆記是定製化的,對你自己定製,你可以用自己最爽的表達方式來描述一個問題,是自己寫給自己看的東西,看了幾遍之後就能非常迅速和容易地理解。之後遇到相同的問題可以快速地通過找筆記解決。
舉個例子,下圖是我記錄的一些關於gdb【linux下調試c++的工具】的使用的一些筆記。我只記錄了我自己最常用的一些內容,也許你看著很亂,但是我就能很容易看懂,這就是我的定製化。
記筆記的習慣一定要堅持,等過個一年或者兩年,這就是你巨大的財富,因為那是只有你才能看懂的東西。我已經記錄了4年多、1G多的內容,現在的筆記基本已經形成了體系,可以給大家展示其中的一部分。
專業知識相關筆記:
開發相關的筆記:
一些類目:
5.有什麼比較好的編程方法?
除了上述分享的一些方法,我認為在同一時間段不要學習太多類別的課程,比如你可以同時學習python和html/css,但是你不要同時學python、操作系統、編譯原理、計算機組成、數據結構、網路,我曾經試過,一門課沒學一會兒就學下一門,其實上一門根本學不到什麼實際的知識。因為記憶知識是符合艾賓浩斯記憶曲線的。對於一門課,特別是很難的專業課,譬如操作系統,你每天看半小時,效果是比較差的,可能你熱身就得半小時。所以寧可每天學兩門,然後每一門學長一點的時間,比如兩小時。【畢竟學校上課,一次課也得兩小時】,要避免貪多,一口吃不成個胖子。
6.我需要刷oj么?
我認為剛開始編程的時候還是應該刷的,但是一定要注意,不要被你周圍的「X神」給誤導了。因為我上大學的時候,身邊總是有很多搞計算機競賽的人,他們之間都互相稱對方為「X神」,某某神又使用一個牛逼的演算法,將程序時間從1秒降低到了0.999秒。我要勸大家的是,刷題不是為了達到這個目的,不是說非要在競賽中拿獎,除非你是特別喜歡,否則,沒必要去背代碼。我們刷題的目的是適應寫代碼的感覺,在這個過程中你會遇到編譯錯誤,你會慢慢去記住一些語法、關鍵字,並理解一些概念,還可以自己去使用它,比如實現數據結構。慢慢的你就會變得有經驗,知道一些錯誤產生的原因。我也是慢慢這樣過來的,我現在在工作和下班以後寫代碼時,基本都不用IDE了,比如寫c++,要麼vim,要麼就是sublime,而調試用的是我前面提到的工具gdb。即,有一個文本編輯器就能寫代碼,脫離了IDE的束縛。
在寫oj之後一段時間,在比較熟練了之後,就可以不去刷題了,可以去譬如github這樣的網站上找點項目來看,然後自己跟著寫一下,編程能力慢慢就提升了。就計算機專業來說,很多同學在大一上完編程課之後,就很少寫代碼了,這樣是很不好的。刷題除了可以鍛煉編程能力,對於找工作前突擊也很有作用。比如,我之前投遞過華為公司的研發崗位,校招的時候有筆試題。我就在16年國慶的時候刷了一下華為的oj,我記得筆試是600分的總分,過100就給面試機會,而我很輕鬆的就拿了500分,而當時也就刷了20多道華為的題。
7.看書還是看視頻?
網上有不少人鄙視看視頻學習的同學,我不知道為什麼,因為我認為看視頻是一個很好的學習方式。不過我們得明白看書和看視頻分別有什麼優缺點。
其實我是很建議看視頻入門的,因為目前網上的應用型【非學術型:比如清華大學的操作系統,非常難】的視頻都是很簡單的,很多是面向初學者的,視頻能用較短的時間告訴你你現在所學的技術可以幹什麼,可能需要先修哪些知識,可以幫助我們搭建一個項目驅動式學習的網路。可是視頻也有個缺點:就是知識非常的雜,很不系統。雖然現在很多教學網站都提供了學習路徑,但是這些路徑中的視頻很多時候都不是同一個老師錄製的,只是按照知識的依賴關係排的順序,所以,如果想通過視頻去系統地學習一門知識,是比較困難的。【當然,一些學術型的視頻還是很推薦的,比如斯坦福的機器學習,清華的操作系統、數據結構等課程,能堅持看完,絕對受益匪淺】。而應用型的,比如web開發等知識,還是得看書。書籍等特點就是系統化,由淺入深,你可以定製化地看自己薄弱的章節。所以一個比較好的學習方式是:
看視頻入門,看書進階。
8.多久能學會編程?
其實這個問題是沒有答案的,如果只是想做出一個小應用,2個月足矣,而就我個人而言,我認為學習編程不是學習一種編程語言,而是學習一個生態,一個計算機系統,所以無止境。
9.我應該選擇什麼資料,看什麼書?
其實這個問題也是很多編程新手容易困惑的問題。網路上擁有我們一輩子都看不完的教程和資料,所以現在應該不會存在找不著視頻教程、找不著書看的問題。而問題就是我們不知道看什麼視頻、看什麼書。從開始學編程到現在,我也買了上百本書,而真正適合自己的好書並不多。而視頻教程的問題就更嚴重了,東看一點、西看一點,知識很難組織成網路。所以學習編程的過程中,我們遇到的最大的問題是:當我們遇到問題的時候,在大量資料面前,我們不知道選擇什麼資料去學習。即使我們使用項目驅動式學習的方法找到了我們的方向,但是同一個路徑下,也有很多資料。前文列舉的項目驅動式學習的圖中,我們是自上而下的去發現問題,然後再解決問題。如果能有人幫我們組織好學習路徑,然後自下而上地去學習,那麼效率可能會提高很多。
推薦閱讀: