《程序员的呐喊》读书笔记
《程序員的吶喊》是Google一位老程序員的經(jīng)驗(yàn)總結(jié),文中展現(xiàn)了他對(duì)各大語(yǔ)言如Java、C/C++、Lisp、Python、Ruby、Perl等的極端觀點(diǎn),比如大力吐槽C++,極力推崇C、Lisp、Ruby。他的觀點(diǎn)只是他個(gè)人經(jīng)驗(yàn)所得,也不一定符合實(shí)情,僅供參考,有任何想法都可以說出來一起討論。
>>>>?
1、程序員和司機(jī)一樣,總是自我安慰說等到需要的時(shí)候再去學(xué)新技能也來得及。但是在內(nèi)心深處他們都明白,其實(shí)當(dāng)需求出現(xiàn)的時(shí)候就已經(jīng)太晚了。因此現(xiàn)實(shí)情況是這樣的,旱鴨子會(huì)和水保持距離,司機(jī)會(huì)繞開泥濘的路段,而程序員會(huì)躲在舒適區(qū)里,搭建圍欄把自己保護(hù)起來,然后祈禱世界和平。
me:其實(shí)喜歡躲在舒適區(qū)里是每個(gè)人都或多或少存在的性格弱點(diǎn),誰(shuí)能克服它誰(shuí)就能學(xué)到新技能,誰(shuí)就能站的比別人高。對(duì)于偶爾努力經(jīng)常懶惰的我來說,技術(shù)也是學(xué)的廣而不精,對(duì)新技術(shù)都有興趣但學(xué)不到精深處,希望剛工作的我可以改善這個(gè)弱點(diǎn)。
2、當(dāng)時(shí)04年亞馬遜正飽受其龐大代碼庫(kù)的困擾,我曾經(jīng)一度認(rèn)為它的代碼庫(kù)失控是因?yàn)檎Z(yǔ)言問題,后來才意識(shí)到企業(yè)文化是主因。首當(dāng)其沖的是,亞馬遜的主流語(yǔ)言里有兩門非常哆嗦的語(yǔ)言C++中和Java,外加一門精練的語(yǔ)言Perl。但是Perl正受到排擠,漸漸退出主流。我覺得這是因?yàn)镻erl程序員能用更少的人力完成和Java/C++程序員同樣的工作量,所以要是比人多的話,他們注定是贏不了的。根據(jù)我們的估算,亞馬遜的代碼量比它的功能數(shù)量膨脹得更快。第二個(gè)因素是,亞馬遜的很多技術(shù)問題完全可以用自定義領(lǐng)域語(yǔ)言(DSL的方式來解決),比如大規(guī)模的查詢、分布式計(jì)算、產(chǎn)品配置等,他們寫了太多不必要的代碼了。我后來跳到Google,發(fā)現(xiàn)他們?yōu)檫@些完全一樣的問題專門編寫了強(qiáng)大的自定義DSL。這證實(shí)了我心中的疑慮,亞馬遜的工程師在這些問題上和無頭蒼蠅沒什么兩樣。我敢說這句話誤傷的概率極低。最后一點(diǎn)就是,和絕大多數(shù)公司一樣,亞馬遜非常抗拒用新語(yǔ)言來解決問題。他們會(huì)避免使用表達(dá)能力更強(qiáng)的通用語(yǔ)言,比如Ruby或Erlang。他們也幾乎從來不會(huì)想到自己去寫DSL。
me:龐大代碼庫(kù)是很多公司都有的弊病吧,可又有多少能想到用DSL來解決呢。害怕變動(dòng)、拒絕新技術(shù)的公司注定是失敗的。
3、為什么C是必修課?
一個(gè)原因是現(xiàn)在的電腦都是馮諾·伊曼結(jié)構(gòu)的,而C以精悍的語(yǔ)法展現(xiàn)了馮·諾伊曼機(jī)的能力,其他類型的機(jī)器也是存在的,比如Lisp機(jī)。還有一個(gè)原因就是,Unix是用C寫的,不僅如此,包括Windows等在內(nèi)的幾乎所有的操作系統(tǒng)都是用C寫成的,因?yàn)樗鼈內(nèi)繉儆隈T諾伊曼機(jī)操作系統(tǒng)。你覺得自己還有其他選擇嗎?至少在操作系統(tǒng)領(lǐng)域里,任何與C迥異的語(yǔ)言都發(fā)揮不出硬件的實(shí)際能力,至少這句話放在近一百年里都是對(duì)的這些系統(tǒng)都誕生于這段時(shí)期內(nèi)。
4、計(jì)算機(jī)編程語(yǔ)言里沒有所謂的“親近生侮謾“,只有在掌握更優(yōu)秀的語(yǔ)言前提下,才會(huì)懂得怎么批判自己最熟悉的那門語(yǔ)言。因此,要是你不喜歡我批評(píng)C++,我建議你去了解一下更優(yōu)秀的語(yǔ)言是什么樣子的,然后你才有資格否定我的話。不過到那時(shí)你就不會(huì)來否定我了,我忽悠成功了。那時(shí)你不會(huì)再喜歡C++,可能會(huì)有點(diǎn)生我的氣,忽悠你討厭自己之前最愛的語(yǔ)言。所以你還是別管我說什么了。C++很出色,非常優(yōu)秀。別在意我的話。它是門很棒的語(yǔ)言。
me:作者是有多么討厭C++啊!“親近生侮謾“的意思是當(dāng)你對(duì)一個(gè)人或事物越親近越熟悉,你就會(huì)越討厭越忽視TA。而作者認(rèn)為計(jì)算機(jī)語(yǔ)言里不會(huì)發(fā)生這種事情,除非你知道了其它更優(yōu)秀的語(yǔ)言。我贊同這個(gè)觀點(diǎn)。
5、亞馬遜的偉大元老們只用兩種語(yǔ)言:C和Lisp。顯然,他們都是Emacs的擁躉。
me:的確很多編程界的大佬都愛這兩門語(yǔ)言,比如《黑客與畫家》的作者。
6、杰米·扎溫斯基曾經(jīng)寫過一篇非常有名的文章來批判Java有多糟糕,但他還是這樣寫道:“先說好的地方:Java沒有free(),我必須承認(rèn)這一點(diǎn),其他錦上添花而已。光這一點(diǎn)就足以讓我忽視其他缺點(diǎn)了,不管它們有多糟糕。有鑒于此,本文接下來的內(nèi)容都可以說無足輕重。”杰米的這篇文章寫于1997年,那時(shí)的Java還在襁褓之中,如今Java早已今非昔比,他當(dāng)時(shí)抱怨的有些東西現(xiàn)在都已經(jīng)修復(fù)了。但也不是全都改好了。就語(yǔ)言層面,Java仍然算不上優(yōu)秀。但正如杰米所言,它“依然是今天最好的語(yǔ)言,遠(yuǎn)遠(yuǎn)比我們?cè)趯?shí)際工作中用的那些徹頭徹尾的垃圾語(yǔ)言要好得多”。不過Java也缺了一些C++的優(yōu)點(diǎn),比如(在棧上)傳引用、typedef,宏,還有重載操作符。這些東西并非必不可少,但是需要的時(shí)候就很方便。對(duì)了還有多重繼承,說得我都開始懷念從前了。假如你要用我自己的“固執(zhí)己見的精靈”來反對(duì)多態(tài),那么我還可以舉出更多為什么多重繼承是必需的例子。有時(shí)間我們可以討論一下“火焰劍”或者“盜賊披風(fēng)”的問題,你就會(huì)明白接口是多糟糕的東西了。幾年前,高斯林自己也承認(rèn),要是有機(jī)會(huì)重來的話,絕對(duì)不會(huì)考慮接口。而這正是Java的問題所在。
me:Java好在垃圾自動(dòng)回收,壞在臃腫不堪,語(yǔ)言本身設(shè)計(jì)的不好。
7、總之,Ruby對(duì)Perl充分實(shí)行了拿來主義。Ruby的作者M(jìn)atz(我沒記錯(cuò)的話,他的本名是松本行弘,不過通常都自稱“Matz”)甚至可能有點(diǎn)借鑒過頭了,連些不好的東西也拿了過來。好在不多,只有一點(diǎn)點(diǎn)而已。基本上Ruby照搬了Perl的字符串處理和Unⅸ集成,語(yǔ)法完全一樣,只此一點(diǎn),Perl的精華就全都有了。這可以說是開了個(gè)去蕪存菁的好頭。接著Matz從Lisp那里吸收了列表處理的精華,從Smalltalk那里拿來了OO,迭代器則是取自CLU,基本上各個(gè)語(yǔ)言里的優(yōu)點(diǎn)都吸收進(jìn)來了。所有的這些東西被完美地糅合在一起,你壓根注意不到斧鑿的痕跡。
me:Ruby本質(zhì)上就是各種語(yǔ)言精華的大雜燴,去蕪存菁,作者精通這么多語(yǔ)言的精華非常值得敬佩。現(xiàn)在的框架也是的,都是互相之間借鑒精華,按需索取,無可厚非。
8、Python本來是有機(jī)會(huì)一統(tǒng)江湖的,但是它有兩個(gè)致命的缺陷:一個(gè)是空白符,另一個(gè)是死腦筋。所謂空白符的問題就是Python的嵌套是通過縮進(jìn)來完成的。它強(qiáng)迫你用特定的方式來縮進(jìn),這樣大家的代碼看起來就是一樣的了。可惜,很多程序員都討厭這個(gè)規(guī)定,感覺好像被剝奪了自由一樣;他們覺得胡亂排版和和編寫那種精簡(jiǎn)到一行沒人看得懂的小程序是自己的權(quán)利,而Python卻侵犯了這一點(diǎn)。Python之父吉多·范羅蘇姆之前也出過幾次昏招,雖然不如拉里那么驚世駭俗,但也真的是夠小兒科的了。比如,Python原本是沒有詞法作用域的。可問題是它連動(dòng)態(tài)作用域也沒有,雖然說動(dòng)態(tài)作用域也有自身的問題,但至少還勉強(qiáng)可以用。Python最早只有全局和局部(函數(shù))作用域,所以雖然它擁有一個(gè)“真正的OO系統(tǒng),可是—個(gè)類卻連自己的實(shí)例變量都沒法訪問。你只能給每個(gè)實(shí)例方法帶上一個(gè)self參數(shù),然后通過self來訪問自己的實(shí)例數(shù)據(jù)。所以你在Python里看到一堆self ,哪怕你忍了空白符,這些self也能把你給逼瘋了。
me:用空格縮進(jìn)的確讓人不爽,不過提升了規(guī)范性。
9、Java 其中特點(diǎn)之一就是凸顯了“架構(gòu)”。Java國(guó)王授予了架構(gòu)尊崇的地位,因?yàn)榧軜?gòu)完全是由名詞組成的。我們都知道,名詞就是事物,而在Java里,事物的地位遠(yuǎn)勝一切動(dòng)作。建筑是由看得見摸得著的事物構(gòu)成的,譬如高聳入云的龐然大物,又如用棍子敲打時(shí)發(fā)出低沉悅耳聲音的東西。Java國(guó)王特別喜歡這種沉悶的聲音,每次換新馬車的時(shí)候,他都特別喜歡從踢輪子中獲得快感。不管上述的兒歌有何瑕疵,它就是不想要任何東西。
me:的確,Java最讓人受不了的就是一堆架構(gòu),一層層封裝致死,調(diào)試起來也麻煩。
10、福勒告訴我們所謂重構(gòu),就是通過迭代,將惡心的代碼變成優(yōu)質(zhì)代碼的藝術(shù)和科學(xué),是能妝點(diǎn)代碼卻不會(huì)在操作過程中產(chǎn)生破壞的算法,而且正確性都是能證明的。
me:作者認(rèn)為福勒寫的重構(gòu)這本書非常不錯(cuò),值得一讀。
11、那么這些代碼一開始是怎么變爛的呢?首先當(dāng)然是由于過早優(yōu)化造成的,為了避免重復(fù)計(jì)算而保存了太多的中間變量。因?yàn)楹ε路椒ㄕ{(diào)用會(huì)造成虛幻的負(fù)擔(dān),而刻意回避編寫短小的函數(shù)。我們還弄出一大堆類的繼承關(guān)系,僅僅是為了想象中可能存在的復(fù)用,為了避免分配器對(duì)象而弄出一個(gè)巨大的參數(shù)列表。濫用null,把它當(dāng)成成具有語(yǔ)義的符號(hào)。放任簡(jiǎn)單的布爾邏輯表達(dá)式變成錯(cuò)綜復(fù)雜、無法閱讀的漿糊。不用訪問方法來封裝數(shù)據(jù)結(jié)構(gòu)。還有其他很多亂七八糟的問題。正是因?yàn)楦鞣N各樣的小錯(cuò)誤別類,加以命名,并歸類成嚴(yán)重錯(cuò)誤。
me:這是福勒重構(gòu)這本書告訴我們的。
12、那我們的代碼是怎么變成那樣的呢?因?yàn)閷懙脿€。這時(shí)重構(gòu)就能救命。再優(yōu)秀的設(shè)計(jì)也會(huì)出紕漏,但我們?nèi)匀豢梢匝a(bǔ)救,反正有自動(dòng)化的奴仆來幫我們修復(fù)這些:小問題。它們不知疲倦,我們只要點(diǎn)個(gè)按鈕就行了。既然如此,誰(shuí)能離得開自動(dòng)化重構(gòu)工具?還有誰(shuí)能協(xié)調(diào)Java那些數(shù)以百計(jì)的小腿,讓它們像毛毛蟲一樣統(tǒng)一行動(dòng)呢?讓我來告訴你答案:Ruby是蝴蝶。(意指Ruby是完全不同的物種,Java中自動(dòng)化重構(gòu)工具所要解決的問題在Ruby中根本不存在。)
me:作者喜歡Ruby,覺得Java需要重構(gòu)是因?yàn)榇a寫的爛,要是用Ruby寫的話根本不需要重構(gòu),也就沒有自動(dòng)化重構(gòu)工具。
13、首先,再垃圾的語(yǔ)言和技術(shù)也一樣有機(jī)會(huì)贏。甚至贏面可能還會(huì)大一點(diǎn),因?yàn)楦恼饋頃?huì)更快。Java擊敗了smalltalk , C++擊敗了Object-C,Perl擊敗了Python,VHS擊敗了Beta,諸如此類。并不是說一項(xiàng)技術(shù)(特別是編程語(yǔ)言)比較優(yōu)秀,它就一定會(huì)勝出。營(yíng)銷才是關(guān)關(guān)鍵。追求公平競(jìng)爭(zhēng)只會(huì)導(dǎo)致你的語(yǔ)言無人問可津。
me:原來一門語(yǔ)言的流行起關(guān)鍵作用的不是這門語(yǔ)言有多優(yōu)秀,而是它的營(yíng)銷做的有多好。嗯,我記得Java就是營(yíng)銷搞起來的。
14、注意,罵誰(shuí)也不能罵Python。相反,罵Ruby罵得最兇的人可能就是Matz自己了。他在自己的演講“為什么Ruby很爛”里,自陳了Ruby的各種問題,當(dāng)時(shí)看得我汗都下來了。不可否認(rèn),任何語(yǔ)言都有缺點(diǎn)。相比之下,我更喜歡Ruby眾的坦誠(chéng),Pyhon那種一味指責(zé)別人,回避問題,過分地自我標(biāo)榜的行為令人感到惡心。
me:作者的意思是Python眾狂妄自大,而Ruby相對(duì)比較坦誠(chéng),這是作者通過經(jīng)過逛兩個(gè)社區(qū)得出的結(jié)論,也不一定準(zhǔn)確。
15、Ruby談不上有多出色,但它現(xiàn)在手上有殺手級(jí)應(yīng)用。Rails對(duì)推動(dòng)Ruby起到了巨大的作用。在Web框架方面,Python可謂輸?shù)靡粩⊥康亍L?hào)稱要和Rails競(jìng)爭(zhēng)的Python框架至少有五個(gè):Pylons、Django、TurboGears、Zope,還有Subway。其實(shí)3個(gè)(甚至4個(gè))都嫌多啊。從營(yíng)銷的角度來講底哪個(gè)比較優(yōu)秀其實(shí)根本不重要,重要的是Python社區(qū)應(yīng)該選中其中一個(gè)后全力鼓吹;否則每個(gè)框架都只只能分到20%,結(jié)果誰(shuí)都沒有實(shí)力跟上Rails的步伐。
me:再一次吐槽Python在web框架方面的不足,話說Python有殺手級(jí)應(yīng)用嗎?
16、Java并沒有提出什么新鮮的東西,它有的SmallTalk早就有了。
me:這種論點(diǎn)好像在哪里聽過。
17、相反我卻親眼見識(shí)了日本服務(wù)生為了滿足那些在商務(wù)旅行中的醉漢所作出的努力,他們的敬業(yè)程度讓我這個(gè)美國(guó)人都感到羞愧。如果要問世界級(jí)的服務(wù)水平是什么樣的,來日本看看就明白了。
me:真的嗎?真的嗎?繼續(xù)學(xué)日語(yǔ),好去日本玩……
18、最終讓Java平臺(tái)占領(lǐng)了那些它做夢(mèng)也沒想到過的領(lǐng)域,一切都虧了這個(gè)所謂的“殺手級(jí)應(yīng)用”Applet。
me:不會(huì)吧???沒聽說過Applet這么厲害,一直覺得它是雞肋。
靜態(tài)類型和動(dòng)態(tài)類型的優(yōu)缺點(diǎn)
1、靜態(tài)類型的優(yōu)點(diǎn)
下面列出了靜態(tài)類型的主要優(yōu)點(diǎn):
(1)靜態(tài)類型可以在程序運(yùn)行之前,依賴其與生俱來的限制來及早發(fā)現(xiàn)一些類型錯(cuò)誤。(或是在插入/更新記錄,解析XML文檔等情況下進(jìn)行檢測(cè)。)
(2)靜態(tài)類型有更多機(jī)會(huì)(或者說更容易)優(yōu)化性能。例如只要數(shù)據(jù)模型完整豐富,那么實(shí)現(xiàn)智能化的數(shù)據(jù)庫(kù)索引就會(huì)更容易一些。編譯器在擁有更精確的變量和表達(dá)式類型信息的情況下可以做出更優(yōu)的決策。
(3)在C++和Java這樣擁有復(fù)雜類型系統(tǒng)的語(yǔ)言里,你可以直接通過查看代碼來確定變量、表達(dá)式、操作符和函數(shù)的靜態(tài)類型。
這種優(yōu)勢(shì)或許在ML和Haskell這樣的類型推導(dǎo)語(yǔ)言里并不明顯,他們顯然認(rèn)為到哪里都要帶著類型標(biāo)簽是缺點(diǎn)。不過你還是可以在有助閱讀理解的情況下標(biāo)明類型一而這些在絕大多數(shù)動(dòng)態(tài)語(yǔ)言里是根本做不到的。
(4)靜態(tài)類型標(biāo)注可以簡(jiǎn)化特定類型的代碼自動(dòng)化處理。比如說自動(dòng)化文檔生成、語(yǔ)法高亮和對(duì)齊、依賴分析、風(fēng)格檢查等各種“讓代碼去解讀代碼”的工作。換句話說,靜態(tài)類型標(biāo)簽讓那些類似編譯器的工具更容易施展拳腳:詞法工具會(huì)有更多明確的語(yǔ)法元素,語(yǔ)義分析時(shí)也比較少要用猜的。
(5)只要看到API或是數(shù)據(jù)庫(kù)結(jié)構(gòu)(而不用去看代碼實(shí)現(xiàn)或數(shù)據(jù)庫(kù)表)就能大致把握到它的結(jié)構(gòu)和用法。
還有其他要補(bǔ)充的嗎?
2、靜態(tài)類型的缺點(diǎn)如下:
(1)它們?nèi)藶榈叵拗屏四愕谋磉_(dá)能力。
比如,Java的類型系統(tǒng)里沒有操作符重載、多重繼承、mix-in、引用參數(shù)、函數(shù)也不是一等公民。原本利用這些技術(shù)可以做出很自然的設(shè)計(jì),現(xiàn)在卻不得不去遷就java的類型系統(tǒng)。無論是Ada還是C++,或是OCaml等任何一種靜態(tài)類型系統(tǒng)都有這樣的問題。差不多半數(shù)的設(shè)計(jì)模式(不光是Gof的那些)都是扭曲原本自然直觀的設(shè)計(jì),好將它們?nèi)M(jìn)某種靜態(tài)類型系統(tǒng):這根本就是方枘圓鑿嘛。
(2)它們會(huì)拖慢開發(fā)進(jìn)度。
事先要?jiǎng)?chuàng)建很多靜態(tài)模型(自頂向下的設(shè)計(jì)),然后還要依據(jù)需求變化不斷修改。這些類型標(biāo)注還會(huì)讓源代碼規(guī)模膨脹導(dǎo)致代碼難以理解,維護(hù)成本上升。(這個(gè)問題只在Java里比較嚴(yán)重,因?yàn)樗恢С纸o類型取別名。)還有就是我上面已經(jīng)提到過的,你得花更多的時(shí)間來調(diào)整設(shè)計(jì),以適應(yīng)靜態(tài)類型系統(tǒng)。
(3)學(xué)習(xí)曲線比較陡。
動(dòng)態(tài)類型語(yǔ)言比較好學(xué)。靜態(tài)類型系統(tǒng)則相對(duì)挑剔,你必須花很多時(shí)間去學(xué)它們建模的方式,外加靜態(tài)類型的語(yǔ)法規(guī)則。另外,靜態(tài)類型錯(cuò)誤(也可以叫編譯器錯(cuò)誤)對(duì)于初學(xué)者來說很難懂,因?yàn)槟菚r(shí)程序根本還沒跑起來呢。你連用printf來調(diào)試的機(jī)會(huì)都沒有,只能撞大運(yùn)似的調(diào)整代碼,祈求能讓編譯器滿意。因此學(xué)習(xí)C++比C和Smalltalk難,OCaml比Lisp難,Nice語(yǔ)言比Java難。而Perl所具備的一系列靜態(tài)復(fù)雜性—各種詭異的規(guī)則,怎么用,什么時(shí)候用等—讓它的難度比Ruby和Python都要高。我從來沒見過有哪門靜態(tài)類型語(yǔ)是很好學(xué)的。
(4)它們會(huì)帶來虛幻的安全感。
靜態(tài)類型系統(tǒng)確實(shí)能減少運(yùn)行時(shí)的錯(cuò)誤,提升數(shù)據(jù)的完整性,所以很容易誤導(dǎo)人們覺得只要能通過編譯讓程序跑起來,那它基本上就沒什么bug了。人們?cè)谟脧?qiáng)靜態(tài)類型系統(tǒng)的語(yǔ)言寫程序時(shí)似乎很少依賴單元測(cè)試,當(dāng)然這也可能只是我的想像罷了。
(5)它們會(huì)導(dǎo)致文檔質(zhì)量下滑。
很多人覺得自動(dòng)生成的javadoc就足夠了,哪怕不注釋代碼也沒關(guān)系, Sourceforge 上充斥著這樣的項(xiàng)目,甚至連Sun JDK也常常有這個(gè)問題。(比如,Sun很多時(shí)候都沒有給static final常量添加javadoc注釋。)
(6)很難用它們寫出兼具高度動(dòng)態(tài)和反射特點(diǎn)的系統(tǒng)。
絕大多數(shù)靜態(tài)類型語(yǔ)言(大概)都出于追求性能的目的,在運(yùn)行時(shí)丟棄了幾乎所有編譯器生成的元數(shù)據(jù)。可是這樣一來這些系統(tǒng)通常也就很難在運(yùn)行時(shí)作出修改(甚至連內(nèi)省都做不到)比如,若要想給模塊加一個(gè)新函數(shù),或是在類里加個(gè)方法,除了重新編譯,關(guān)閉程序然后重啟之外別無他法。受此影響的不單是開發(fā)流程整個(gè)設(shè)計(jì)理念也難逃波及。你可能需要搭建個(gè)復(fù)雜的架構(gòu)來支持動(dòng)態(tài)功能而這些東西會(huì)無可避免地和你的業(yè)務(wù)代碼混在一起。
3、動(dòng)態(tài)類型的優(yōu)缺點(diǎn):
只要把上面的列表對(duì)調(diào)一下,你基本上就可以列出動(dòng)態(tài)類型語(yǔ)言的優(yōu)缺點(diǎn)了。動(dòng)態(tài)語(yǔ)言的表達(dá)能力更強(qiáng),設(shè)計(jì)靈活度也更大;易學(xué)易用,開發(fā)速度快;通常運(yùn)行時(shí)的靈活性也更高。相對(duì)地,動(dòng)態(tài)語(yǔ)言無法及時(shí)給出類型錯(cuò)誤(至少編譯器做不到),性能調(diào)優(yōu)的難度也比較高,很難做自動(dòng)化靜態(tài)分析,另外,變量和表達(dá)式的類型在代碼里很不直觀,沒辦法一眼看出來。
靜態(tài)語(yǔ)言最終會(huì)向用戶屈服開始添加一些動(dòng)態(tài)特性,而動(dòng)態(tài)語(yǔ)言常常也會(huì)嘗試引入一下可選的靜態(tài)類型系統(tǒng)(或是靜態(tài)分析工具),此外它們還會(huì)設(shè)法改善性能增加錯(cuò)誤檢測(cè),以便及早發(fā)現(xiàn)問題。很遺憾,除非一開始設(shè)計(jì)語(yǔ)言的時(shí)候就考慮到可選的靜態(tài)類型,否則強(qiáng)扭的瓜怎么也不會(huì)甜的。
強(qiáng)類型與弱類型系統(tǒng)的較量
1、下面我會(huì)以稍微有點(diǎn)戲謔的方式解釋這兩種理念(指的是強(qiáng)類型和弱類型)的工作流程,盡可能將它們本質(zhì)區(qū)別展現(xiàn)出來。
強(qiáng)類型陣營(yíng)基本是這樣工作的:首先是按照當(dāng)前的需求進(jìn)行設(shè)計(jì);制定出文檔哪怕只是初稿也沒關(guān)系;然后定義接口和數(shù)據(jù)模型。假設(shè)系統(tǒng)要承受巨大流量,因此每個(gè)地方都要考慮性能。避免采用垃圾收集和正則表達(dá)式這類抽象。(注意:即便是Java程序員,通常也會(huì)努力避免觸發(fā)垃圾收集,他們總是在開始寫程序之討論對(duì)象池的問題。)
他們只有在無計(jì)可施的情況下才會(huì)考慮動(dòng)態(tài)類型。例如,一支采用Corba的團(tuán)隊(duì)只有在極端情況下才會(huì)在每個(gè)接口調(diào)用上添加一個(gè)XML字符串參數(shù),這樣他們就能繞開當(dāng)初選擇的死板的類型系統(tǒng)了。
第二個(gè)陣營(yíng)基本是這樣工作的:先搭建原型。只要你寫代碼的速度比寫同等詳細(xì)程度的文檔快,你就可以更早地從用戶那里獲得反饋。按照當(dāng)下的需求定義合理的接口和數(shù)據(jù)模型,但是別在上面浪費(fèi)太多時(shí)間。一切以能跑起來為準(zhǔn),怎么方便怎么來。假設(shè)自己肯定要面對(duì)大量的需求變化,所以每個(gè)地方首先考慮的是盡快讓系統(tǒng)運(yùn)行起來。能用抽象的地方就盡量用(比如如每次都去收集數(shù)據(jù)而先不考慮緩沖,能用正則的地方就先不用字符串比較)就算明明知是牛刀也沒關(guān)系,因?yàn)槟銚Q回的是更大的靈活性。代碼量比較少,通常bug的數(shù)量也會(huì)更少。
他們只有在被逼無奈的情況下才會(huì)進(jìn)行性能調(diào)優(yōu)以及禁止修改接口和數(shù)據(jù)定義。例如,一支Perl團(tuán)隊(duì)可能會(huì)將一些關(guān)鍵的核心模塊用C重寫,然后創(chuàng)建XS綁定。時(shí)間—長(zhǎng),這些抽象就漸漸變成了既定標(biāo)準(zhǔn),它們被包裹在數(shù)據(jù)定義和細(xì)致的OO接口里,再也無法修改。(就算是Perl程序員也常常會(huì)忍不住祭出銀彈,為常用的抽象編寫OO接口)
那你覺得最終采用這些策略的結(jié)果會(huì)怎么樣?
設(shè)計(jì)模式
1、不過現(xiàn)在大家都清醒過來了,不是嗎?設(shè)計(jì)模式不是特性。工廠不是特性,委托、代理、橋接也都不是。它們只是提供了漂亮的盒子,以松散的方式來裝載特性。但是別忘了,盒子、袋子和隔板自己也是要占用空間的。設(shè)計(jì)模式也不例外(至少在“四人幫”的書里所介紹的大多數(shù)模式都是這樣)。更悲劇的是“四人幫”模式里唯一能精簡(jiǎn)代碼的解釋器(Interpreter)模式卻被那些恨不得把設(shè)計(jì)模式紋在身上的程序員忽略了。
依賴注入是另一個(gè)新型的Java設(shè)計(jì)模式,Ruby、Python、Perl還有Javascript,程序員大概聽都沒聽過吧。就算他們聽過,他們也能正確地得出他們根本不需要這種玩意兒的結(jié)論。依賴注入是一種驚人的描述式架構(gòu),讓Java能在某些方面和更高級(jí)的語(yǔ)言一樣,變得更動(dòng)態(tài)一點(diǎn)。你猜得沒錯(cuò),依賴注入會(huì)讓Java代碼變得更大。變大是Java中無法回避的東西。成長(zhǎng)是生活的一部分。Java就像是俄羅斯方塊,不過積木和積木之間的空隙都填不滿,結(jié)果只能越堆越高。
me:現(xiàn)在Java程序員相信都知道依賴注入了,因?yàn)樗匾?#xff0c;用在各大框架里,比如spring,依賴注入使得能夠在文件里配置類及其各種關(guān)系,當(dāng)然使得Java更靈活更強(qiáng)大了。
程序員需要了解的是哪些數(shù)學(xué)分支?
1、實(shí)際生活中,計(jì)算機(jī)科學(xué)家常用的數(shù)學(xué)和上面那個(gè)列表幾乎沒有重疊。其一,小學(xué)和中學(xué)里教的絕大部分?jǐn)?shù)學(xué)都是連續(xù)的,也就是實(shí)數(shù)上的數(shù)學(xué)。而對(duì)計(jì)算機(jī)科學(xué)家來說,95%有趣的數(shù)學(xué)都是離散的,也就是整數(shù)上的數(shù)學(xué)。
me:程序員所要解決的數(shù)學(xué)問題一般都是離散數(shù)學(xué),其中最有用的課程應(yīng)該就是組合數(shù)學(xué)和概率論統(tǒng)計(jì)。
2、除了概率論和離散數(shù)學(xué),其他數(shù)學(xué)分支也是有助于程序員的。可惜除非你去輔修數(shù)學(xué),否則學(xué)校是不會(huì)教你的。它們包含了:
(1) 統(tǒng)計(jì)。我的離散數(shù)學(xué)書里講到了一點(diǎn)。但是統(tǒng)計(jì)是一門完整的學(xué)科,而且是非常重要的學(xué)科,重要到根本不需要額外介紹。
(2)代數(shù)和線性代數(shù)(比如矩陣)。線性代數(shù)應(yīng)該緊跟在代數(shù)后面教。它不是很難,而且在很多領(lǐng)域都非常非常有用,比如機(jī)器學(xué)習(xí)。
(3)數(shù)理邏輯。
(4)信息論和柯氏復(fù)雜度。信息論(粗略地講)主要是關(guān)于數(shù)據(jù)壓縮的,而柯氏復(fù)雜度(同樣粗略地講)則是關(guān)于算法的復(fù)雜度(比如最小空間是多少,需要多長(zhǎng)時(shí)間,程序或者數(shù)據(jù)結(jié)構(gòu)有多優(yōu)雅等)的。它們都是好玩,有趣,實(shí)用的學(xué)科。
當(dāng)然還有其他的分支,而且有些學(xué)科互有重疊。但重點(diǎn)在于:對(duì)你有用的數(shù)學(xué)和學(xué)校覺得有用的數(shù)學(xué)是非常不同的。
3、微積分的本質(zhì)就是連續(xù)一變化的速度,曲線下的面積,固體的體積。很有用,記憶和很多煩瑣的步驟程序員通常不需要這些東西。知道大致概但是需要大量的概念和技巧就可以了,細(xì)節(jié)方面等到需要的時(shí)候再查也來得及。
編譯器,你懂嗎?
1、我在招人的時(shí)候有一個(gè)訣竅。就是在尋找優(yōu)秀的軟件工程師“通才”的時(shí)候,通常在簡(jiǎn)歷上你可以看到到各種讓你覺得不行的關(guān)鍵字和詞,但“編譯器”是我唯一感興趣的詞。
me:作者強(qiáng)烈要求程序員學(xué)編譯器原理,你還記得嗎?
2、編譯器會(huì)接收一串符號(hào)流,根據(jù)預(yù)先定義好的規(guī)則,分析出這串符號(hào)的結(jié)構(gòu),然后把它轉(zhuǎn)換成另一串符號(hào)流。是不是很籠統(tǒng)?的確是。一幅圖片能不能被當(dāng)成是符號(hào)流?當(dāng)然可以。它可以是每一行像素所組成的流。每個(gè)像素就是一個(gè)數(shù)字。每個(gè)數(shù)字就是一個(gè)符號(hào)。編譯器當(dāng)然可以轉(zhuǎn)換圖片。英語(yǔ)可以被當(dāng)做符號(hào)流叫嗎?當(dāng)然可以。規(guī)則或許會(huì)很復(fù)雜,但是自然語(yǔ)言處理的確可以被看成是某種很炫的編譯。
編譯過程中第一個(gè)大階段就是解析,即把輸入的內(nèi)容變成一棵樹。中間要經(jīng)過預(yù)處理,詞法分析(也叫單詞化)然后是語(yǔ)法分析和中間代碼生成這幾個(gè)步驟。詞法分析通常是由正則表達(dá)式來完成的。語(yǔ)法分析則是根據(jù)語(yǔ)法完成。你可以采遞歸向下(最常見)或是解析器生成器(在小語(yǔ)言中比較常見)或是更炫的算來實(shí)現(xiàn),只不過相應(yīng)的執(zhí)行速度也會(huì)慢一點(diǎn)。無論如何,最后的結(jié)果通常都是某解析樹。
第二個(gè)大階段是類型檢查。這是一群狂熱的學(xué)術(shù)分子(包括他們的組織以及或者手下的研究生)他們自信可以寫出非常聰明的程序,能分析出你的程序想干什么,并且在你出錯(cuò)的時(shí)候幫你指出。不過奇怪的是,他們并不覺得自己是在研究人工智能畢竟人工智能界已經(jīng)(明智地)放棄確定性的方法了。
第三個(gè)陣營(yíng)是代碼生成,他們通常都被邊緣化了。只要你對(duì)遞歸有足夠的了解,知道自己的祖先不是亞當(dāng)和夏娃,那么代碼生成還是挺直觀的。這里要講的其實(shí)是優(yōu)化就是那種生成足夠正確的代碼,讓絕大多數(shù)用戶都意識(shí)不到有問題的藝術(shù)。等等不好意思,這是亞馬遜化。優(yōu)化是指根據(jù)你那些昂貴的菜鳥程序員寫出來的垃圾代碼生成“正確”代碼的藝術(shù)。
保守派和自由派,你屬于哪派?
1、軟件工程有自己的政治軸心,—端是保守派,另—端是自由派。
畢竟“保守的”這個(gè)形容詞基本上和謹(jǐn)慎、厭惡風(fēng)險(xiǎn)就是同義詞。金融上的保守主義常常(也是顯而易見的)和年齡以及財(cái)富聯(lián)系在一起。公司會(huì)隨著時(shí)間逐漸變得保守起來,因?yàn)樗鼈儼具^過了各種法律訴訟、技術(shù)失敗、公共危機(jī)、金融風(fēng)暴等危機(jī)。連螞蟻和蚱蜢的寓言故事都告訴我們寒冬將至,要儲(chǔ)存食物。
本質(zhì)上,保守主義就是風(fēng)險(xiǎn)管理。
同樣自由派的觀點(diǎn)常常和年輕、理想主義、天真無邪聯(lián)系在一起。在企業(yè)里,創(chuàng)業(yè)公司往往是典型的自由派,一部分原因是他們本來就是為了(在一定程度上)改變世界而存在的(而自由主義原本就意味著變化),另一部分則是他們必須全力以赴完成投資人設(shè)定的目標(biāo),所以放棄一點(diǎn)軟件安全也就變得合理(不得已)了。
me:保守派,盡量修復(fù)所有bug,回避錯(cuò)誤,學(xué)不會(huì)新語(yǔ)法,通過編譯器安全檢查,數(shù)據(jù)必須遵循事先定義好的格式,公共接口必須嚴(yán)格建模,生產(chǎn)系統(tǒng)里絕不允許存在危險(xiǎn)過有風(fēng)險(xiǎn)的后門,安全性有疑慮就不能上線,快比慢好,注重性能。自由派則相反。
2、各大語(yǔ)言的分派:(作者自己使用語(yǔ)言的經(jīng)驗(yàn),僅供參考)
難以言喻的自由:匯編語(yǔ)言
極端自由:Perl、Ruby、PHP、腳本
非常自由:Javascript、VB、Lua
自由:Python、Common Lisp、Smalltalk/Sqeak
溫和自由:C、Object-C、Schema
溫和保守:C++、Java、C#、D、Go
保守:Clojure、Erlang、Pascal
非常保守:Scala、Ada、Ocaml、Eiffel
極端保守:Haskell、SML
3、(1)Facebook是極端自由的。他們主要用的是C++和PHP,他們的數(shù)據(jù)都放在memcached里:只有鍵值對(duì),沒有數(shù)據(jù)庫(kù)結(jié)構(gòu)。他們把數(shù)據(jù)導(dǎo)出來放到一個(gè)后臺(tái)Hⅳe數(shù)據(jù)倉(cāng)庫(kù)里,然后用Hadoop來進(jìn)行離線數(shù)據(jù)分析。每?jī)蓚€(gè)星期左右他們?nèi)匀粫?huì)舉辦通宵黑客馬拉松,反正他們的程序員大多都是單身男青年(至少我上次去參觀的時(shí)候還是如此),股票的估值也還很高(我上次查價(jià)格的時(shí)候好像已經(jīng)沒那么好了)。作為一家公司,Facebook是非常緊密的,具有很強(qiáng)的執(zhí)行力,十分注重程序員在網(wǎng)站上發(fā)布新功能的單兵能力,沒有什么官僚主義。這對(duì)一家規(guī)模這么大、用戶那么多多的公司來講是難能可貴的。保守派毫無疑問會(huì)厭惡蔑視他們。但是Facebook證明了不管具有什么世界觀的程序員,只要聯(lián)合起來,就能解決很多問題。
(2)Amazon是自由的。
(3)Google是保守的。開始是有點(diǎn)自由的 ,然后就變得越來越保守了。只有在剛剛開始的時(shí)候才是軟件自由的,那時(shí)候的搜索引擎是用Python寫的。隨著公司不斷壯大,他們很快就轉(zhuǎn)向了軟件保守主義,而這完全是由工程師自己主導(dǎo)的。他們寫了很多宣言警告太多語(yǔ)言所帶來的危險(xiǎn),而僅有的幾門語(yǔ)言里,也里,也有嚴(yán)格的風(fēng)格指南,限制使用那些端保守,險(xiǎn)”或者“難以閱讀”的語(yǔ)言特性。
(4)微軟是難以言喻的保守。
來源:程序媛想事
算法數(shù)學(xué)之美微信公眾號(hào)歡迎賜稿
稿件涉及數(shù)學(xué)、物理、算法、計(jì)算機(jī)、編程等相關(guān)領(lǐng)域。
稿件一經(jīng)采用,我們將奉上稿酬。
投稿郵箱:math_alg@163.com
總結(jié)
以上是生活随笔為你收集整理的《程序员的呐喊》读书笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 睡眠音频分割及识别问题(四)--YAMN
- 下一篇: coverage path planni