對的,我這裏要講的不是如何掌握一種程序語言,而是全部的……程序員
不少編程初學者至今還在給我寫信請教,問我該學習什麼程序語言,怎麼學習。因爲我知道如何掌握「全部」的程序語言,老是感受這種該學「一種」什麼語言的問題比較低級,因此一直沒來得及回覆他們 :P 但是逐漸的,我發現原來不僅是小白們有這個問題,就連美國大公司的不少資深工程師,其實也沒搞明白。正則表達式
今天我有動力了,想來統一回答一下這個擱置已久的「初級問題」。相似的話題貌似曾經寫過,然而如今我想把它從新寫一遍。由於在跟不少人交流以後,我對本身頭腦中的(未轉化爲語言的)想法,有了更精準的表達。編程
若是你存在如下的種種困惑,那麼這篇文章也許會對你有所幫助:設計模式
雖然我已經再也不過問這些世事,然而不容置疑的現實是,程序語言仍然是很重要的話題,這個狀況短期內不會改變。程序員的崗位每每會要求熟悉某些語言,甚至某些奇葩的公司要求你「深刻理解 OOP 或者 FP 設計模式」。對於在職的程序員,程序語言至今仍然是能夠爭得面紅耳赤的宗教話題。它的宗教性之強,以致於我在批評和調侃某些語言(好比 Go 語言)的時候,有些人會本能地覺得我是另一種語言(好比 Java)的粉絲。函數
顯然我不多是任何一種語言的粉絲,我甚至不是 Yin 語言的粉絲 ;) 對於任何從沒見過的語言,我都是直接拿起來就用,而不須要通過學習的過程。看了這篇文章,也許你會明白我爲何能夠達到這個效果。理解了這裏面的東西,每一個程序員都應該能夠作到這一點。嗯,希望吧。工具
不少人在意本身或者別人是否「會」某種語言,對「發明」了某種語言的人倍加崇拜,爲各類語言的孰優孰劣爭得面紅耳赤。這些問題對於我來講都是不存在的。雖然我寫文章批評過很多語言的缺陷,在實際工做中我卻不多跟人爭論這些。若是有其它人在我身邊爭論,我甚至會戴上耳機,都懶得聽他們說什麼 ;) 爲何呢?我發現歸根結底的緣由,是由於我重視的是「語言特性」,而不是整個的「語言」。我能用任何語言寫出不錯的代碼,就算再糟糕的語言也差不了多少。學習
任何一種「語言」,都是各類「語言特性」的組合。打個比方吧,一個程序語言就像一臺電腦。它的牌子可能叫「聯想」,或者「IBM」,或者「Dell」,或者「蘋果」。那麼,你能夠說蘋果必定比 IBM 好嗎?你不能。你得看看它裏面裝的是什麼型號的處理器,有多少個核,主頻多少,有多少 L1 cache,L2 cache……,有多少內存和硬盤,顯示器分辨率有多大,顯卡是什麼 GPU,網卡速度,等等各類「配置」。有時候你還得看各個組件之間的兼容性。spa
這些配置對應到程序語言裏面,就是所謂「語言特性」。舉一些語言特性的例子:設計
這些語言特性,就像你在選擇一臺電腦的時候,看它裏面是什麼配置。選電腦的時候,沒有人會說 Dell 必定是最好的,他們只會說這個型號裏面裝的是 Intel 的 i7 處理器,這個比 i5 的好,DDR3 的內存 比 DDR2 的快這麼多,SSD 比磁盤快不少,ATI 的顯卡是垃圾…… 如此等等。指針
程序語言也是同樣的道理。對於初學者來講,其實不必糾結到底要先學哪種語言,再學哪種。曾經有人給我發信問這種問題,糾結了好幾個星期,結果一個語言都還沒開始學。有這糾結的時間,其實均可以把他糾結過的語言所有掌握了。
初學者每每不理解,每一種語言裏面必然有一套「通用」的特性。好比變量,函數,整數和浮點數運算,等等。這些是每一個通用程序語言裏面都必須有的,一個都不能少。你只要經過「某種語言」學會了這些特性,掌握這些特性的根本概念,就能隨時把這些知識應用到任何其它語言。你爲此投入的時間基本不會浪費。因此初學者糾結要「先學哪一種語言」,這種時間花的很不值得,還不如隨便挑一個語言,跳進去。
若是你不能用一種語言裏面的基本特性寫出好的代碼,那你換成另一種語言也無濟於事。你會寫出同樣差的代碼。我常常看到有些人 Java 代碼寫得至關亂,至關糟糕,卻罵 Java 很差,雄心勃勃要換用 Go 語言。這些人沒有明白,是否能寫出好的代碼在於人,而不在於語言。若是你的心中沒有清晰簡單的思惟模型,你用任何語言表述出來都是一堆亂麻。若是你 Java 代碼寫得很糟糕,那麼你寫 Go 語言代碼也會同樣糟糕,甚至更差。
不少初學者不瞭解,一個高明的程序員若是開始用一種新的程序語言,他每每不是去看這個語言的大部頭手冊或者書籍,而是先有一個須要解決的問題。手頭有了問題,他能夠用兩分鐘瀏覽一下這語言的手冊,看看這語言大概長什麼樣。而後,他直接拿起一段例子代碼來開始修改搗鼓,想法把這代碼改爲本身正想解決的問題。在這個簡短的過程當中,他很快的掌握了這個語言,並用它表達出內心的想法。
在這個過程當中,隨着需求的出現,他可能會問這樣的問題:
注意到了嗎?上面每個引號裏面的內容,都是一種語言特性(或者叫概念)。這些概念能夠存在於任何的語言裏面,雖然語法可能不同,它們的本質都是同樣的。好比,有些語言的參數類型寫在變量前面,有些寫在後面,有些中間隔了一個冒號,有些沒有。
這些實際問題都是隨着寫實際的代碼,解決手頭的問題,天然而然帶出來的,而不是一開頭就抱着語言手冊看得仔仔細細。由於掌握了語言特性的人都知道,本身須要的特性,在任何語言裏面必定有對應的表達方式。若是沒有直接的方式表達,那麼必定有某種「繞過方式」。若是有直接的表達方式,那麼它只是語法稍微有所不一樣而已。因此,他是帶着問題找特性,就像查字典同樣,而不是被淹沒於大部頭的手冊裏面,昏昏欲睡一個月纔開始寫代碼。
掌握了通用的語言特性,剩下的就只剩某些語言「特有」的特性了。研究語言的人都知道,要設計出新的,好的,無害的特性,是很是困難的。因此通常說來,一種好的語言,它所特有的新特性,終究不會超過一兩種。若是有個語言號稱本身有超過 5 種新特性,那你就得當心了,由於它們帶來的和可能不是優點,而是災難!
一樣的道理,最好的語言研究者,每每不是某種語言的設計者,而是某種關鍵語言特性的設計者(或者支持者)。舉個例子,著名的計算機科學家 Dijkstra 就是「遞歸」的強烈支持者。如今的語言裏面都有遞歸,然而你可能不知道,早期的程序語言是不支持遞歸的。直到 Dijkstra 強烈要求 Algol 60 委員會加入對遞歸的支持,這個局面才改變了。Tony Hoare 也是語言特性設計者。他設計了幾個重要的語言特性,卻沒有設計過任何語言。另外你們不要忘了,有個語言專家叫王垠,他是早期 union type 的支持者和實現者,也是 checked exception 特性的支持者,他在本身的博文裏指出了 checked exception 和 union type 之間的關係 :P
不少人盲目的崇拜語言設計者,只要聽到有人設計(或者美其民曰「發明」)了一個語言,就熱血沸騰,佩服的五體投地。他們卻沒有理解,其實全部的程序語言,不過是像 Dell,聯想同樣的「組裝機」。語言特性的設計者,纔是像 Intel,AMD,ARM,Qualcomm 那樣核心技術的創造者。
因此初學者要想事半功倍,就應該從一種「合理」的,沒有明顯嚴重問題的語言出發,掌握最關鍵的語言特性,而後由此把這些概念應用到其它語言。哪些是合理的入門語言呢?我我的以爲這些語言均可以用來入門:
那麼相比之下,我不推薦用哪些語言入門呢?
總的說來,你不該該使用所謂「腳本語言」做爲入門語言,特別是那些源於早期 Unix 系統的腳本語言工具。PowerShell 雖然比 Unix 的 Shell 有所進步,然而它仍然沒有擺脫腳本語言的根本問題——他們的設計者不知道他們本身在幹什麼 :P
採用腳本語言學編程,一個很嚴重的問題就是使得學習者抓不住關鍵。腳本語言每每把一些系統工具性質的東西(好比正則表達式,Web 概念)加入到語法裏面,致使初學者爲它們浪費太多時間,卻沒有理解編程最關鍵的概念:變量,函數,遞歸,類型……
不推薦 Go 語言的緣由相似,雖然 Go 語言不算腳本語言,然而他的設計者顯然不明白本身在幹什麼。因此使用 Go 語言來學編程,你不能專一於最關鍵,最好的語言特性。
爲了達到我以前提到的融會貫通,一通百通的效果,初學者應該專一於語言裏面最關鍵的特性,而不是被次要的特性分心。
舉個誇張點的例子。我發現不少編程培訓班和野雞大學的編程入門課,每每一來就教學生如何使用 printf 打印「Hello World!」,進而要他們記憶 printf 的各類「格式字符」的意義,要他們實現各類複雜格式的打印輸出,甚至要求打印到文本文件裏,而後再讀出來……
但是卻不知,這種輸出輸入操做其實根本不算是語言的一部分,並且對於掌握編程的核心概念來講,都是次要的。有些人的 Java 課程進行了好幾個星期,竟然還在佈置各類 printf 的做業。學生寫出幾百行的 printf,卻不理解變量和函數是什麼,甚至連算術語句和循環語句都不知道怎麼用!這就是爲何不少初學者感受編程很難,我連 %d
,%f
,%.2f
的含義都記不住,還怎麼學編程!
然而這些野雞大學的「教授」頭銜是如此的洗腦,以致於被他們教過的學生(好比我女友)到我這裏請教,竟然罵我淨教一些沒用的東西,學了連 printf 的做業都無法完成 :P 你別跟我講 for 循環,函數什麼的了…… 可不能夠等幾個月,等我背熟了 printf 的用法再學那些啊?
因此你就發現一旦被差勁的老師教過,這個程序員基本就毀了。就算遇到好的老師,他們也很難糾正過來。
固然這是一個誇張的例子,由於 printf 根本不算是語言特性,但這個例子從一樣的角度說明了次要膚淺的語言特性帶來的問題。
這裏舉一些次要語言特性的例子:
func foo(s string, x, y, z int, c bool) { ... }
在基本學會了各類語言特性,能用它們來寫代碼以後,下一步的進階就是去實現它們。只有實現了各類語言特性,你才能徹底地擁有它們,成爲它們的主人。不然你就只是它們的使用者,你會被語言的設計者牽着鼻子走。
有個大師說得好,徹底理解一種語言最好的方法就是本身動手實現它,也就是本身寫一個解釋器來實現它的語義。但我以爲這句話應該稍微修改一下:徹底理解一種「語言特性」最好的方法就是本身親自實現它。
注意我在這裏把「語言」改成了「語言特性」。你並不須要實現整個語言來達到這個目的,由於咱們最終使用的是語言特性。只要你本身實現了一種語言特性,你就能理解這個特性在任何語言裏的實現方式和用法。
舉個例子,學習 SICP 的時候,你們都會親自用 Scheme 實現一個面向對象系統。用 Scheme 實現的面向對象系統,跟 Java,C++,Python 之類的語言語法相去甚遠,然而它卻能幫助你理解任何這些 OOP 語言裏面的「面向對象」這一律念,它甚至能幫助你理解各類面向對象實現的差別。
這種效果是你直接學習 OOP 語言得不到的,由於在學習 Java,C++,Python 之類語言的時候,你只是一個用戶,而用 Scheme 本身動手實現了 OO 系統以後,你成爲了一個創造者。
相似的特性還包括類型推導,類型檢查,惰性求值,如此等等。我實現過幾乎全部的語言特性,因此任何語言在個人面前,都是能夠被任意拆卸組裝的玩具,而再也不是凌駕於我之上的神聖。
寫了這麼多,重要的話重複三遍:語言特性,語言特性,語言特性,語言特性!無論是初學者仍是資深程序員,應該專一於語言特性,而不是糾結於整個的「語言品牌」。只有這樣才能達到融會貫通,拿起任何語言幾乎當即就會用,而且寫出高質量的代碼。