王垠:如何掌握所有的程序語言
CSDN
2小時前
或許王垠是后者,但不妨礙看到有才華人的一些思考斩熊。本文是他博客剛更新的一篇里逆,分享給大家进胯。
來源:當然我在扯淡, 作者:王垠运悲, 點擊“閱讀原文” 查看原文。
對的项钮,我這里要講的不是如何掌握一種程序語言班眯,而是所有的……
很多編程初學者至今還在給我寫信請教,問我該學習什么程序語言烁巫,怎么學習署隘。由于我知道如何掌握所有的程序語言,我總感覺這種“該學什么語言”的問題是如此低級亚隙,所以一直沒來得及回復 :P 可是逐漸的磁餐,我發(fā)現(xiàn)原來不只是小白們有這個問題,就連美國大公司的很多資深工程師阿弃,其實也沒搞明白诊霹。
今天休閑活動進入第二個星期,稍微閑下來一點渣淳,我想來統(tǒng)一回答一下這個擱置已久的“初級問題”脾还。這個話題貌似曾經(jīng)寫過,然而現(xiàn)在我想把它重新寫一遍入愧。因為在跟很多人交流之后鄙漏,我對自己頭腦中的(未轉化為語言的)想法,有了更精準的表達棺蛛。
如果你存在以下的種種困惑怔蚌,那么這篇文章也許會對你有所幫助:
你是編程初學者,不知道該選擇什么程序語言來入門旁赊。
你是資深的程序員或者團隊領導桦踊,對新出現(xiàn)的種種語言感到困惑,不知道該“投資”哪種語言终畅。
你的團隊為使用哪種程序語言爭論不休钞钙,發(fā)生各種宗教斗爭。
你追逐潮流采用了某種時髦的語言声离,結果兩個月之后發(fā)現(xiàn)深陷泥潭芒炼,痛苦不堪……
雖然我已經(jīng)不再過問這些世事,然而無可置疑的現(xiàn)實是术徊,程序語言仍然是很重要的話題本刽,這個情況短時間內不會改變。程序員的崗位往往會要求熟悉某些語言,甚至某些奇葩的公司要求你“深入理解 OOP 或者 FP 設計模式”子寓。對于在職的程序員暗挑,程序語言至今仍然是可以爭得面紅耳赤的宗教話題。它的宗教性之強斜友,以至于我在批評和調侃某些語言(比如 Go 語言)的時候炸裆,有些人會本能地以為我是另外一種語言(比如 Java)的粉絲。
顯然我不可能是任何一種語言的粉絲鲜屏,我甚至不是 Yin 語言的粉絲 ;) 對于任何從沒見過的語言烹看,我都是直接拿起來就用,而不需要經(jīng)過學習的過程洛史」呤猓看了這篇文章,也許你會明白我為什么可以達到這個效果也殖。理解了這里面的東西土思,每個程序員都應該可以做到這一點。嗯忆嗜,但愿吧己儒。
重視語言特性,而不是語言
很多人在乎自己或者別人是否“會”某種語言捆毫,對“發(fā)明”了某種語言的人倍加崇拜址愿,為各種語言的孰優(yōu)孰劣爭得面紅耳赤。這些問題對于我來說都是不存在的冻璃。雖然我寫文章批評過不少語言的缺陷响谓,在實際工作中我卻很少跟人爭論這些。如果有其它人在我身邊爭論省艳,我甚至會戴上耳機娘纷,都懶得聽他們說什么 ;) 為什么呢?我發(fā)現(xiàn)歸根結底的原因跋炕,是因為我重視的是“語言特性”赖晶,而不是整個的“語言”。我能用任何語言寫出不錯的代碼辐烂,就算再糟糕的語言也差不了多少遏插。
任何一種“語言”,都是各種“語言特性”的組合纠修。打個比方吧胳嘲,一個程序語言就像一臺電腦。它的牌子可能叫“聯(lián)想”扣草,或者“IBM”了牛,或者“Dell”颜屠,或者“蘋果”。那么鹰祸,你可以說蘋果一定比 IBM 好嗎甫窟?你不能。你得看看它里面裝的是什么型號的處理器蛙婴,有多少個核粗井,主頻多少,有多少 L1 cache街图,L2 cache……浇衬,有多少內存和硬盤,顯示器分辨率有多大台夺,顯卡是什么 GPU径玖,網(wǎng)卡速度痴脾,等等各種“配置”颤介。有時候你還得看各個組件之間的兼容性。
這些配置對應到程序語言里面赞赖,就是所謂“語言特性”滚朵。舉一些語言特性的例子:
變量定義
算術運算
for 循環(huán)語句,while 循環(huán)語句
函數(shù)定義前域,函數(shù)調用
遞歸
靜態(tài)類型系統(tǒng)
類型推導
lambda 函數(shù)
面向對象
垃圾回收
指針算術
goto 語句
這些語言特性辕近,就像你在選擇一臺電腦的時候,看它里面是什么配置匿垄。選電腦的時候移宅,沒有人會說 Dell 一定是最好的,他們只會說這個型號里面裝的是 Intel 的 i7 處理器椿疗,這個比 i5 的好漏峰,DDR3 的內存 比 DDR2 的快這么多,SSD 比磁盤快很多届榄,ATI 的顯卡是垃圾…… 如此等等浅乔。
程序語言也是一樣的道理。對于初學者來說铝条,其實沒必要糾結到底要先學哪一種語言靖苇,再學哪一種。曾經(jīng)有人給我發(fā)信問這種問題班缰,糾結了好幾個星期贤壁,結果一個語言都還沒開始學。有這糾結的時間埠忘,其實都可以把他糾結過的語言全部掌握了芯砸。
初學者往往不理解萧芙,每一種語言里面必然有一套“通用”的特性。比如變量假丧,函數(shù)双揪,整數(shù)和浮點數(shù)運算,等等包帚。這些是每個通用程序語言里面都必須有的渔期,一個都不能少。你只要通過“某種語言”學會了這些特性渴邦,掌握這些特性的根本概念疯趟,就能隨時把這些知識應用到任何其它語言。你為此投入的時間基本不會浪費谋梭。所以初學者糾結要“先學哪種語言”信峻,這種時間花的很不值得,還不如隨便挑一個語言瓮床,跳進去盹舞。
如果你不能用一種語言里面的基本特性寫出好的代碼,那你換成另外一種語言也無濟于事隘庄。你會寫出一樣差的代碼踢步。我經(jīng)常看到有些人 Java 代碼寫得相當亂丑掺,相當糟糕获印,卻罵 Java 不好,雄心勃勃要換用 Go 語言街州。這些人沒有明白兼丰,是否能寫出好的代碼在于人,而不在于語言唆缴。如果你的心中沒有清晰簡單的思維模型鳍征,你用任何語言表述出來都是一堆亂麻。如果你 Java 代碼寫得很糟糕琐谤,那么你寫 Go 語言代碼也會一樣糟糕蟆技,甚至更差。
很多初學者不了解斗忌,一個高明的程序員如果開始用一種新的程序語言质礼,他往往不是去看這個語言的大部頭手冊或者書籍,而是先有一個需要解決的問題织阳。手頭有了問題眶蕉,他可以用兩分鐘瀏覽一下這語言的手冊,看看這語言大概長什么樣唧躲。然后造挽,他直接拿起一段例子代碼來開始修改搗鼓碱璃,想法把這代碼改成自己正想解決的問題。在這個簡短的過程中饭入,他很快的掌握了這個語言嵌器,并用它表達出心里的想法。
在這個過程中谐丢,隨著需求的出現(xiàn)爽航,他可能會問這樣的問題:
這個語言的“變量定義”是什么語法,需要“聲明類型”嗎乾忱,還是可以用“類型推導”讥珍?
它的“類型”是什么語法?是否支持“泛型”窄瘟?泛型的 “variance” 如何表達衷佃?
這個語言的“函數(shù)”是什么語法,“函數(shù)調用”是什么語法蹄葱,可否使用“缺省參數(shù)”氏义?
……
注意到了嗎?上面每一個引號里面的內容新蟆,都是一種語言特性(或者叫概念)觅赊。這些概念可以存在于任何的語言里面右蕊,雖然語法可能不一樣琼稻,它們的本質都是一樣的。比如饶囚,有些語言的參數(shù)類型寫在變量前面帕翻,有些寫在后面,有些中間隔了一個冒號萝风,有些沒有嘀掸。
這些實際問題都是隨著寫實際的代碼,解決手頭的問題规惰,自然而然帶出來的睬塌,而不是一開頭就抱著語言手冊看得仔仔細細。因為掌握了語言特性的人都知道歇万,自己需要的特性揩晴,在任何語言里面一定有對應的表達方式。如果沒有直接的方式表達贪磺,那么一定有某種“繞過方式”硫兰。如果有直接的表達方式,那么它只是語法稍微有所不同而已寒锚。所以劫映,他是帶著問題找特性违孝,就像查字典一樣,而不是被淹沒于大部頭的手冊里面泳赋,昏昏欲睡一個月才開始寫代碼雌桑。
掌握了通用的語言特性,剩下的就只剩某些語言“特有”的特性了祖今。研究語言的人都知道筹燕,要設計出新的,好的衅鹿,無害的特性撒踪,是非常困難的。所以一般說來大渤,一種好的語言制妄,它所特有的新特性,終究不會超過一兩種泵三。如果有個語言號稱自己有超過 5 種新特性耕捞,那你就得小心了,因為它們帶來的和可能不是優(yōu)勢烫幕,而是災難俺抽!
同樣的道理,最好的語言研究者较曼,往往不是某種語言的設計者磷斧,而是某種關鍵語言特性的設計者(或者支持者)。舉個例子捷犹,著名的計算機科學家 Dijkstra 就是“遞歸”的強烈支持者〕诜梗現(xiàn)在的語言里面都有遞歸,然而你可能不知道萍歉,早期的程序語言是不支持遞歸的侣颂。直到 Dijkstra 強烈要求 Algol 60 委員會加入對遞歸的支持,這個局面才改變了枪孩。Tony Hoare 也是語言特性設計者憔晒。他設計了幾個重要的語言特性,卻沒有設計過任何語言蔑舞。另外大家不要忘了拒担,有個語言專家叫王垠,他是早期 union type 的支持者和實現(xiàn)者斗幼,也是 checked exception 特性的支持者澎蛛,他在自己的博文里指出了 checked exception 和 union type 之間的關系 :P
很多人盲目的崇拜語言設計者,只要聽到有人設計(或者美其民曰“發(fā)明”)了一個語言蜕窿,就熱血沸騰谋逻,佩服的五體投地呆馁。他們卻沒有理解,其實所有的程序語言毁兆,不過是像 Dell浙滤,聯(lián)想一樣的“組裝機”。語言特性的設計者气堕,才是像 Intel纺腊,AMD,ARM茎芭,Qualcomm 那樣核心技術的創(chuàng)造者揖膜。
合理的入門語言
所以初學者要想事半功倍,就應該從一種“合理”的梅桩,沒有明顯嚴重問題的語言出發(fā)壹粟,掌握最關鍵的語言特性,然后由此把這些概念應用到其它語言宿百。哪些是合理的入門語言呢趁仙?我個人覺得這些語言都可以用來入門:
Scheme
C
Java
Python
JavaScript
那么相比之下,我不推薦用哪些語言入門呢垦页?
Shell
PowerShell
AWK
Perl
PHP
Basic
Go
總的說來雀费,你不應該使用所謂“腳本語言”作為入門語言,特別是那些源于早期 Unix 系統(tǒng)的腳本語言工具痊焊。PowerShell 雖然比 Unix 的 Shell 有所進步盏袄,然而它仍然沒有擺脫腳本語言的根本問題——他們的設計者不知道他們自己在干什么 :P
采用腳本語言學編程,一個很嚴重的問題就是使得學習者抓不住關鍵宋光。腳本語言往往把一些系統(tǒng)工具性質的東西(比如正則表達式貌矿,Web 概念)加入到語法里面炭菌,導致初學者為它們浪費太多時間罪佳,卻沒有理解編程最關鍵的概念:變量,函數(shù)黑低,遞歸赘艳,類型……
不推薦 Go 語言的原因類似,雖然 Go 語言不算腳本語言克握,然而他的設計者顯然不明白自己在干什么蕾管。所以使用 Go 語言來學編程,你不能專注于最關鍵菩暗,最好的語言特性掰曾。
掌握關鍵語言特性,忽略次要特性
為了達到我之前提到的融會貫通停团,一通百通的效果旷坦,初學者應該專注于語言里面最關鍵的特性掏熬,而不是被次要的特性分心。
舉個夸張點的例子秒梅。我發(fā)現(xiàn)很多編程培訓班和野雞大學的編程入門課旗芬,往往一來就教學生如何使用 printf 打印“Hello World!”捆蜀,進而要他們記憶 printf 的各種“格式字符”的意義疮丛,要他們實現(xiàn)各種復雜格式的打印輸出,甚至要求打印到文本文件里辆它,然后再讀出來……
可是殊不知誊薄,這種輸出輸入操作其實根本不算是語言的一部分,而且對于掌握編程的核心概念來說锰茉,都是次要的暇屋。有些人的 Java 課程進行了好幾個星期,居然還在布置各種 printf 的作業(yè)洞辣。學生寫出幾百行的 printf咐刨,卻不理解變量和函數(shù)是什么,甚至連算術語句和循環(huán)語句都不知道怎么用扬霜!這就是為什么很多初學者感覺編程很難定鸟,我連 %d,%f著瓶,%.2f 的含義都記不住联予,還怎么學編程!
然而這些野雞大學的“教授”頭銜是如此的洗腦材原,以至于被他們教過的學生(比如我女朋友)到我這里請教沸久,居然罵我凈教一些沒用的東西,學了連 printf 的作業(yè)都沒法完成 :P 你別跟我講 for 循環(huán)余蟹,函數(shù)什么的了…… 可不可以等幾個月卷胯,等我背熟了 printf 的用法再學那些啊威酒?
所以你就發(fā)現(xiàn)一旦被差勁的老師教過窑睁,這個程序員基本就毀了。就算遇到好的老師葵孤,他們也很難糾正過來担钮。
當然這是一個夸張的例子,因為 printf 根本不算是語言特性尤仍,但這個例子從同樣的角度說明了次要膚淺的語言特性帶來的問題箫津。
這里舉一些次要語言特性的例子:
C 語言的語句塊,如果里面只有一條語句,可以不打花括號苏遥。
Go 語言的函數(shù)參數(shù)類型如果一樣可以合并在一起寫送挑,比如 func foo(s string, x, y, z int, c bool) { ... }
Perl 把正則表達式作為語言的一種特殊語法
JavaScript 語句可以在某些時候省略句尾的分號
Haskell 和 ML 等語言的 currying
自己動手實現(xiàn)語言特性
在基本學會了各種語言特性,能用它們來寫代碼之后暖眼,下一步的進階就是去實現(xiàn)它們惕耕。只有實現(xiàn)了各種語言特性,你才能完全地擁有它們诫肠,成為它們的主人司澎。否則你就只是它們的使用者,你會被語言的設計者牽著鼻子走栋豫。
有個大師說得好挤安,完全理解一種語言最好的方法就是自己動手實現(xiàn)它,也就是自己寫一個解釋器來實現(xiàn)它的語義丧鸯。但我覺得這句話應該稍微修改一下:完全理解一種“語言特性”最好的方法就是自己親自實現(xiàn)它蛤铜。
注意我在這里把“語言”改為了“語言特性”。你并不需要實現(xiàn)整個語言來達到這個目的丛肢,因為我們最終使用的是語言特性围肥。只要你自己實現(xiàn)了一種語言特性,你就能理解這個特性在任何語言里的實現(xiàn)方式和用法蜂怎。
舉個例子穆刻,學習 SICP 的時候,大家都會親自用 Scheme 實現(xiàn)一個面向對象系統(tǒng)杠步。用 Scheme 實現(xiàn)的面向對象系統(tǒng)氢伟,跟 Java,C++幽歼,Python 之類的語言語法相去甚遠朵锣,然而它卻能幫助你理解任何這些 OOP 語言里面的“面向對象”這一概念,它甚至能幫助你理解各種面向對象實現(xiàn)的差異甸私。
這種效果是你直接學習 OOP 語言得不到的诚些,因為在學習 Java,C++颠蕴,Python 之類語言的時候泣刹,你只是一個用戶,而用 Scheme 自己動手實現(xiàn)了 OO 系統(tǒng)之后犀被,你成為了一個創(chuàng)造者。
類似的特性還包括類型推導外冀,類型檢查寡键,惰性求值,如此等等雪隧。我實現(xiàn)過幾乎所有的語言特性西轩,所以任何語言在我的面前员舵,都是可以被任意拆卸組裝的玩具,而不再是凌駕于我之上的神圣藕畔。
總結
寫了這么多马僻,重要的話重復三遍:語言特性,語言特性注服,語言特性韭邓,語言特性!不管是初學者還是資深程序員溶弟,應該專注于語言特性女淑,而不是糾結于整個的“語言品牌”。只有這樣才能達到融會貫通辜御,拿起任何語言幾乎立即就會用鸭你,并且寫出高質量的代碼。