隨手扎
【心得筆記】「松本行弘的程序世界」讀書筆記
這是一本「不只是Ruby的Ruby技術書籍」,要我在下另一個註腳的話,應該會是「從Ruby看計算機概論」。內容涵蓋之廣,物件導向程式設計、動態程式語言、原形程式設計(Propotype-base programming)、鴨子型別(算是一種設計模式)、強大的元編程(Meta Programming)、設計模式、有趣的猴子補丁、文字編碼、Ajax網頁技術、正則表達式(Rege)、平行處理(多工)、資訊安全、資料持久化(Serialization)、函數型程式設計(Functional Programming)。儘管只有快400頁,但可以從一本書裡窺看不同角度的技術世界。自己升大學以前,曾經在暑假看了至少三本計算機概論,好了解自己未來所選的科系,大學更是又讀了一次,在我看來,這本「松本行弘的程序世界」簡直就是技術界的計算機概論。對於只學過一種編程想法(如結構化程式設計或是物件導向程式設計)的人來說,或許會有許多地方看不懂,就當做個入門。不過,我自己到是看的頗為愉快。之前就有人說:Ruby的開發者是語言學家、Python的開發者是數學家。我看完本書後,松本行弘的視野真的很多角度、很廣。研究學習程式語言也是我的興趣之一,看在本書的當下,真覺得我和松大哥很投緣。
上面順序主要照書內出現的順序,下面稍微歸類整理一下:
- 程式語言設計思想
- 動態程式語言
- 物件導向程式設計(Object-oriented programming)
- 原形程式設計(Propotype-base programming)
- 函數型程式設計(Functional Programming)
- 開發設計思想
- 設計模式(Design Pattern)
- 鴨子型別(算是一種設計模式)
- 有趣的猴子補丁(Monkey Pitching)
- 強大的元編程(Meta Programming)
- 計算機概論常見內容
- 文字編碼
- 正則表達式(Rege)
- Ajax網頁技術
- 平行處理(多工)
- 資訊安全
- 資料持久化(Serialization)
這本書我在 2019/12/28 其實就看完了,不過跑去試了一些其他東西,到今天才來寫心得筆記。(看得當下其實就很想寫了🐰)
推薦指數:★★★★★
程式語言的重要性
最根本的理由是:語言體現了人類思源的本質。
– Page.2
一個語言,可以又不同說法。但是不同語言之前,思想差異會更大。在與朋友進行的30日LeetCode馬拉松更深有感覺(有些題目貌似還有些東西沒看到,未來也許會整理個)。 光是Python,不同結構寫法,就可以明顯體現撰寫程式的人,當下思考的差異。Python的設計思想是「There should be one– and preferably only one –obvious way to do it.」。同樣是Python,不同人寫已是如此,使用不同語言的話尤是。
為什麼松本行弘開發Ruby?
Ruby程式語言的設計目標是,讓作為語言設計者的我能夠輕鬆編成,進而提高開發效率。
– Page.3
果然科技進步來自惰性阿😂。
但是,相比中低階的程式語言,高階程式語言有助於開發人員,從更高層次,更容易、更好的思考解決問題,但這不代表了解低階程式語言、計算機組織的運作架構是不必要的,我認為,這有助於優化。相關思考筆記和優化章節有關。(以及浮點數容易引起誤差)
編程語言並不是以安全的角度減少程序員出錯,而是在程序員自己負責的前提下為他提供最大限度發揮能力的靈活性。
– Page.5
Ruby沒有Lisp的macro
這段原本想下標:「Ruby v.s. Lisp」。但其實我只想提一兩點而已。
Ruby並沒有Lisp裡Macro的能力,儘管我看來程序塊block
有點像了。我認同濫用Macro會照成嚴重分歧,但作為一個Lisper,我喜愛的,正是Common Lisp賦予開發人員近乎完全的能力。這也是為什麼我沒深度的去學習Clojure,因為他沒有Read-Macro。(不過對於Scheme的設計思想還是去了解了一下,一些Clojure看起來方便的東西我也有看,然後思考如何在Common Lisp使用)
當然了,我知道世界上有很多Lisp程序員並不受此之累,他們正式通過面向特定程序訂製語言而最大限度地提高了開發效率。不過在我個人來看,他們只是極少數的一部分程序員。
– Page.6
(我也想成為那一小部份😂)
Ruby是用什麼寫的?
p.11, p.368
松本行弘本身是一個C的程式人員,故第一個Ruby實現,也是用C。現在應該還是可以自行編譯來使用Ruby。雖然不像C/C++、Common Lisp,先有了規範,才有實做,但也與Python相似的,有多個Ruby的實現。
開發語言 | Ruby | Python | Common Lisp | 備註 |
---|---|---|---|---|
C | Ruby | CPython | ecl | |
Java | JRuby | Jython | abcl | 具我所知Jython只支援到Python2的語法,已經沒有在更新 |
.Net Framework(CLI) | IronRuby | IronPython | ||
self | Rubinius | PyPy | sbcl | (下面 備註1 1, 2 ) |
※ Lua也有LuaJ、eLua
※ 有趣的是,EMCAScript目前是有規範的,但是實現感覺更常走在規範前面。常見的實現有:V8、Node.js、Duktape。
原形設計 v.s. 模板設計
p.21
我才知道我原來就有一點點 模糊 的原形導向程式設計的概念。著名的有EMCAScript,不過我個人受Lua啟發學習,看到這段我還特別回去翻了一下「Lua程序設計」。這讓我更清楚的了解這部份的設計。
※ 我還稍微玩了一下EMCAScript裡的this
、Lua 5.2後的_ENV
和Ruby的self
。(如果我還記得的話,也許會做一下紀錄)
猴子補丁(Monkey Pitching)
猴子補丁(Monkey Pitching)很有趣、很好玩。
class A # define A class
def initialize(name)
@name = name
end
def hello() # hello() method
puts "Hello, " + @name
end
end
a = A.new "World"
a.hello # Output: Hello, World
class A # it isn't re-defining class A. that jsut open it.
def bye() # add new method bey
puts "Bye, " + @name
end
end
a.hello # the method hello still can use.
a.bye # call new method, Output: Bye, World
第一個class A
定義了新的Class A,但第二個並非重新定義Class A,而只是打開他,並加入新的方法。(當然你或許可以覆寫(override) initialize
)
這同樣是一種設計模式(Design Pattern),是一種思想。Python也可以做得到:
class A:
def __init__(self, name: str):
self.name = name
def hello(self):
print("Hello, " + self.name)
a = A("World")
a.hello()
A.bye = lambda self: print("Bye, " + self.name)
a.hello()
a.bye()
不過Ruby的遠比Python來的更強大、更自由,當然也更危險。Ruby甚至可以去增加內部型態(Built-in type)的方法。
ActiveSupport 庫像打猴子補丁那樣對整數類追加了weeks、ago這些方法,這種大膽性可以說正是Ruby on Rails的重要特點。
本人從以前就開始使用Ruby,這種大膽讓我感覺到有點暈。
– Page. 166
猴子補丁 松哥自己也覺得暈www
不過不得不說,對於中文為母語的我,看到2019.year + 12.month + 28.day
,可以直接理解成2019年12月28日。相比英文在說年的時候使用Year 2019
,這樣寫我更喜歡。
※ C#裡似乎有部份(partial
)類別來做類似猴子補丁的事情
安全猴子補丁的幾個基本原則
p.161
Ruby的猴子補丁是強大的,也是危險的。
書中提到幾個安全猴子補丁的幾個基本原則、建議:
- 基本上只是追加功能
- 進行功能變更時要慎重,盡可能小規模
- 小心互相作用
Ruby可能不適合大規模開發
Ruby可能不適合大規模開發,原因是:
- 編譯時不做類型檢查
- 沒有包(package)
- 存在開放類別
儘管如此,只要開發人員遵守一定原則,在配合上一些工具,大規模開發還是有可能的。(據悉Ruby正在討論像是TypeScript標記型別的辦法)
※ Python 3.6後可以hint類別。之後可以用mypy
等工具去做檢查。
※ Python同樣有上面部份提到的缺陷,但Python的大規模開發目前算是常見的。
浮點數容易引起誤差
p.235
電腦計算浮點數的方式不像人一般思考的那樣,https://0.30000000000000004.com/ 這有趣的網址,內容就說明了一部份程式語言內的現象。Ruby可以透過猴子補丁的方式,加入BigDecimal、Rational。
性能優化
優化性能有幾個原則:
過早優化是萬惡之源
以及
- 別做優化
- (僅適用於專家)先別做優化
– Page. 248
儘管性能優化的成就感讓人感到充實,但還是得考慮到演算法和內置實現的優劣。在用Python解LeetCode時就有所體會,明明從演算法來看,少執行了幾個指令,但是內置的就是比較快。
優化的方向
p.253
- 減少物件
- 減少調用
- 利用立即值
平行處理模型
p.267
一般在作業系統、計算機概論學到的,同常是使用互斥鎖(Mutex)。不過在思想設計層面,可以分成 鎖模型 和 列隊模型。
書中列隊模型舉的例子是Erlang提供的Active Model(參與者模型)。在我看來Golang的Channel也是屬於列隊模型,而且寫起來還蠻爽的。
異常處理的歷史
Ruby並不使用C家常見的try-catch
做異常捕獲。松本行弘認為begin-rescue
更符合行為上的名字。
最初使哪種語言開始提供異常處理功能的?很遺憾,我沒能得到確切答案。我推測是Lisp,要碼就是與其相近的某種語言。Lisp從1972年就有了異常處理能力,而Java是20年才誕生的。3
– Page.292
Common Lisp裡的異常處理更是強大,有能力做重起(restart
)。Python雖然可以像這樣寫,出錯時直接進入debugger,但似乎沒我想想的強大:
# https://stackoverflow.com/questions/13174412/python-start-interactive-debugger-when-exception-would-be-otherwise-thrown
if __name__ == '__main__':
try:
raise Exception()
except:
import pdb
pdb.set_trace()
C/C++安全性檢查工具
p.358
C/C++提供了低階的操作能力,尤其指標運算更是特別。但也因此容易寫出危險的程式,目前也有許多工具協助檢查:
記憶體回收的演算法
p.360
自己看過Lua的實現(我可能還會再去翻書一次),還有一部份Python的實現。對於記憶體回收的演算法我有印象的只有計數器和雙串鏈結構演算法。大部分時候不需要去了解這部份演算法,此只是簡單紀錄一下:
- 引用計數方式
- 標記和掃除方式
- 標記和緊縮方式
- 複製方式
- 分代記憶體回收
- 保守記憶體回收
- 增量記憶體回收
- bitmap標紀
擴展的方法、工具
p.377
在Ruby是用什麼寫的?一節提到,多數語言都有提供與C交互的方式。此外像是Java的Java Native Interface(JNI)和Java Native Access(JNA)。除了原生的方式,Ruby也有一些方法可以更簡單的擴充語言:
- RubyInline
- Ricsin
- dl
- ffi
此外還有SWIG。libffi也是許多語言會使用到的方案之一。
題外話
備註1:C本身通常也是C+組語寫的;Rust從很早開始就用Rust開發;SBCL本身可以用任何Common Lisp實現編譯更是讓我驚豔,那過程我可能一輩子忘不了(雖然有報一些錯,沒編完)。基本上自編譯(Self-Compile)需要自己做Boost,還有一堆限制在。 ↩︎
儘管大多數語言都有與C交互的方案,以C寫的Ruby、Python、Lua便有使用C擴充語言本身的能力,有時用於瓶頸加速使用。使用非官方的版本,可能會有一些三方擴充套件無法使用。以Python來說,著名的Numpy原本無法在PyPy使用。(我印象中後來可以了) ↩︎
Java是1995年面市。 ↩︎
LeetCode就用gcc自帶的Sanitizer去做檢查。 ↩︎