《代碼之髓--編程語言核心概念》讀書筆記

《代碼之髓--編程語言核心概念》

[日] 西尾泰和(Nishio Hirokazu) 著

曾一鳴 譯

人民郵電出版社  2014年8月第1版

書中的示例源代碼可以從作者的網站上下載: http://nhiro.org/langbook/

p3: ruby語言中false, nil爲假,其餘都爲真(包括數字0)。c語言中0爲假,非0爲真。python語言中0爲假,空的容器(如空字符串,空的列表等)爲假。java語言中0不是布爾值,所以既不是真也不是假。所以,0爲真,0爲假,0即非真也非假的語言都存在。

p5: 在招聘網站dice.com上檢索一下,就可知道APL語言的沒落了。

p8: 1946年,第一臺電子計算機---ENIAC(埃尼阿克,Electronic Numerical Integrator and Computer)問世。它是第一臺可編程計算機。編程方式就是用電纜把不同的端口連接起來。修改程序很費勁。

1949年,EDSAC(愛達賽克,Electronic Delay Storage Automatic Calculator, 電子延遲存儲自動計算機)問世。它是通過紙帶打孔的方式來記錄和讀取數據。程序通過紙帶輸入。修改程序簡單了很多。

paper tape : https://en.wikipedia.org/wiki/File:PaperTapes-5and8Hole.jpg

作者設計的,基於瀏覽器的EDSAC仿真器: http://nhiro.org/learn_language/repos/EDSAC-on-browser/index.html

p10: FORTRAN的全稱是Formula Translating System(公式翻譯系統)。設計者是John Backus。

P11: Perl語言設計者Larry Wall在其著作Programming Perl(中文版爲《Perl語言編程》中國電力出版社,何偉平 譯)中提出,程序員的3大美德:懶惰、急躁和傲慢(laziness, impatience and hubris)。

p17: Forth語言開發與1958年左右,是一種幾乎沒有語法的語言。設計者是Charles H. Moore。

作者的網頁上Forth模擬器:http://nhiro.org/learn_language/FORTH-on-browser.html

p24: 前綴表達式和後綴表達式也被稱爲波蘭表示法和逆波蘭表示法,得名於最先研究它們的波蘭人Jan Lukasiewicz。前綴表達式總括號是不必要的。

p28,p42: if...else..., while, for, foreach語句都是爲了更可讀而發明的,只用條件跳轉就可以實現這些功能。

p40: 函數在不同時期,不同語言中,有事務、程序(procedure)、子程序(subroutine)、方法(method)等不同叫法,都是一回事。

p43: 函數的誕生。把反覆使用的命令封裝在一起再利用,這種需求在很早以前就有了。1949年的EDSAC就使用了帶有這一功能的技術。參見 https://www.cl.cam.ac.uk/~mr10/edsacposter.pdf。那時函數的返回正確的地址是靠每次調用前修改在函數結尾中的跳轉指令的目的地址。調用處要知道返回命令的地址,以便修改其中的返回地址。假設函數增加了幾條語句,所有調用的地方都要修改。

後來設計專用的寄存器以保存返回地址,並同時設計有跳轉到該寄存器所記錄的地址的命令,這樣就調用處就不用知道函數的返回命令的地址了,只要填寫返回地址寄存器就可以了。但是,這種方法也有一個問題,如果調用函數X其間又調用了函數Y,則返回地址就會被覆蓋,函數X執行之後應該返回的目的地址就找不到了。

最終的解決辦法是使用棧。

p45:函數名使一組操作更可讀。

p47: 過去有些語言無法實現遞歸調用,現在幾乎所有的語言都支持這一編程技術。

p54: 出錯時如何處理,大體上可以分爲2中方法:使用返回值和使用異常處理。除了c語言外,大多數語言都支持異常處理。

使用返回值處理錯誤有2個問題:1)遺漏錯誤。忘了或者懶得檢查返回值,並做處理。比如,c語言中寫文件的函數fprintf(),它在執行錯誤時返回負值。你對這個檢查過嗎?還是想當然地以爲執行成功從而遺漏了錯誤呢?一個很少會出錯的函數可能通過很多測試,真正發現問題,可能是產品發佈很久以後了。由於時間久遠、又是在遠程的生產環境下,追查問題往往會很辛苦。此外,由於連鎖反應,問題看起來是在和出錯的函數沒有任何關係的地方發現的,這樣一來就更難發現真正的問題所在了。理性情況下,嚴格檢查返回的錯誤值,並處理,就不會造成遺漏錯誤的情況。然而,現實中忘記或忽略檢查返回值的情況不勝枚舉。2)處理返回錯誤代碼,會使得源代碼很難讀懂。常常是少量正常處理的代碼夾在大量的錯誤處理代碼的縫隙中間,使得程序流程難以讀懂。把錯誤處理代碼集中起來,用goto語句跳轉到錯誤處理代碼,是個好辦法。參見Linus Torvalds的文章 http://www.linuxfromscratch.org/alfs/view/hacker/part2/hacker/coding-style.html

p60: PL/I語言中異常和失敗叫做條件(condition),拋出異常的命令不是現在一般的raise或throw,而是signal。

p61: John Goodenough 1975年提出了現代異常的處理方式: 一是明確聲明命令可能拋出的異常,二是需要有將可能出錯的操作括起來的語句結構。參見 https://www.sei.cmu.edu/about/leadership/display.cfm?customel_datapageid_2623=3002

p62: CLU語言使用了except一詞,但是CLU語言中拋出異常的命令和PL/I語言一樣,都是signal。C++語言選用throw作爲觸發異常的命令,是因爲raise和signal兩個詞已經在標準庫中作爲函數名字佔用了。於是觸發異常的表述就變成了拋出異常。

p65: 1990年左右,微軟公司開始引入結構化異常處理(SEH, __try, __except, __finally),1995年java也引入finally。現今,python和ruby語言也支持finally。

p66: c++中沒有finally,使用的是RAII(資源獲取即初始化),Bjarne Stroustrup認爲,RAII比finally更優雅。

p67: 2001年出現的D語言,引入了作用域守護(scope guard)的概念,定義從某作用域退出時要執行的操作。

p70: 同一種錯誤,是返回錯誤碼,還是拋出異常,不同語言有不同的做法。在生產環境下,不同的軟件目的不同,簡單地停止操作有時可能不太妥當。但是,作者認爲,至少是在學習和開發階段,一出錯就立刻拋出異常,終止操作立刻報告是較好的,這種思想被成爲錯誤優先(fail first)。

p73: 檢查型異常沒有得到普及,參見C#語言的設計者Anders Hejlsberg所說“The Trouble with Checked Exceptions” https://www.artima.com/intv/handcuffs.html

p88: Perl語言中,local的變量是動態作用域(從perl4開始),my的變量是靜態作用域(從perl5開始)

p89: Python語言的內置作用域是其他語言的所謂全局作用域,python的全局作用域是文件作用域。

p97: 位值制計數法發明於印度,途徑阿拉伯國家傳到歐洲,因此被稱爲阿拉伯數字。有一本年代可考的著作是花剌子模(al-khwarizmi)於公元825年寫的《印度數的計算法》,這是關於數位最古老的記載。值得一提的是,al-khwarizmi這個詞在拉丁語裏被記爲Algoritmi,這就是現代英語中算法(algorithm)的詞源。

p99: calculate的詞源是拉丁語中表達算盤中的算珠的詞calculus。Calcium(鈣)一詞的詞源同樣來於此。

p132: java、python、ruby將數組作爲一種最基本的容器,lisp、scheme、haskell將鏈表作爲最基本的容器。

p147:  表明源碼編碼方式的魔術註釋符。

emacs:

# -*- coding: shift_jis -*-

vim:

# vim: set fileencoding=shift_jis :

p150: c語言只規定了char類型最低爲8bit,IBM7.01(1952年)和UNIVAC 1103(1953年)採用了9bit。但是時至21世紀的今天,8bit的機器佔了絕大多數。char是character(字符)的前4個字母。

p161: 《Java Concurrency in Practice》 Brain Goetz, Joshua Bloch, Doug Lea 著,中文版《Java併發編程實戰》 機械工業出版社 童雲蘭 譯。競爭發生的3個條件:a)共享變量 b)修改變量  c)同時訪問。只要任何一個條件不具備,就可以避免競爭。

避免條件a。採用進程(不共享內存,當然訪問文件、數據庫等還可能競爭)。或者採用actor模型,即傳遞消息來通信,而不共享內存。Erlan、Scala就採用了actor模型。

避免條件b。c/c++中const聲明的變量,Scala中val聲明的變量,Java中Immutable模式(就是類的私有成員,不提供setter)下的變量都是不能改寫的。從而對它們的訪問不存在競爭。

避免條件c。採用協作式多任務的模式。即一個任務主動在適當的時候,讓出資源,讓別人使用。這並不總能夠容易做到。另一種方式是用鎖(lock,mutex,semaphore都是鎖一類的東西)

p176:模塊和包是一個意思,就是把相關的變量和函數放在一起,便於管理。Perl、Java叫包。Python、Ruby叫模塊。

p188: JavaScript中函數f前面使用new運算符後,會執行下面4個操作:

創建新的對象x;新創建的對象x的原型變爲函數f的原型;把新創建的對象x傳給this,執行函數f的內容;返回對象x。

p213:發表於2002年的一篇關於Trait的論文很好地整理的類的兩種截然相反的作用。一種是用於創建實例的作用,它要求類是全年的、包含所有必需的內容的、大的類。另一種作用是作爲再利用單元的作用,它要求類是按功能分的、沒有多餘內容的、小的類。參見:Nathanael Schaerli, Stephane Ducasse, Oscar Nierstrasz, Andrew Black. "Traits: Composable Units of Behaviour", 2002.

p216: Perl 6也引入了和Trait類似的概念,叫做Roll。PHP也從5.4版開始引入了這種功能。Ruby在2.0版引入了mix method, 可以像Trait一樣操作模塊。

[2019-01-14]

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章