碎言碎語
- 和前面的 ML 和 Racket 感覺明顯不一樣了,一邊學著一邊覺得這真是一門奇怪的語言,有著各種奇怪的語法,不過真的算是一個奇妙的體驗(相比前面的兩門語言,Ruby 的學習資源多了不少)。
- week 1 的作業直接就是給出一份 Ruby 源碼的俄羅斯方塊游戲,而任務就是給這個游戲添加功能,趣味性不用多說,也能很好的考察到閱讀代碼、應用已有代碼的能力。不得不再次感嘆作業真的用心。
- week 2 首先就是比較,OOP vs FP,大量的代碼示例以及詳細的講解,但絕不是為了告訴我們用什么而不用什么,而是在什么時候該用什么,使用的優缺點。而作業則涉及了 SML 和 Ruby 兩門語言,我們嘗試去用這兩門編程語言去完成同一件事情。
筆記
1. Ruby is a pure object-oriented language, which means all values in the language are objects.
2. 動態的類定義
即使我們得到了一個類實例化后的對象,在后面我們去修改這個類的方法(只需要重寫這個類中的那個我們需要修改的方法),之前的對象的方法也會改變,那么就會有一個問題。
class Adef q03enddef q1q0 + 12end
enda = A.newputs a.q1class Adef q0"as"end
endputs a.q1
此時就會報錯:TypeError: no implicit conversion of Fixnum into String
。
然后我們可以有一個操作:
class Fixnumdef +1end
end
將上面的代碼復制到 1.rb 文件并保存。
在 REPL 中輸入 load "1.rb"
。
REPL直接崩潰了,報錯:in '+': wrong number of arguments (1 for 0) (ArgumentError)
。
3. Ruby 中的 Blocks 和 Proc Class
在 Ruby 中 Blocks 很類似于函數式編程語言中的閉包(closures),或者說功能上很類似匿名函數,可以傳遞給一個函數,在函數內部執行,或者結合數組自帶的那些方法使用(類似于函數式編程語言中的高階函數)。
比如可以這樣:
10.times { puts "HI" }
10 是一個 Fixnum 類實例化后的對象,它自帶 times 方法,功能么就是執行后面的 Blocks 里面的代碼 10 次。
但是 Blocks 并不是對象,當然你也不能辨別它屬于哪個類,所以它無法賦值給一個變量,或者放到一個數組里,或者作為參數傳遞給一個函數。這個時候就需要 Proc 類。
它實例化后的對象就是lambda {}
。
lambda {}.class # Proc
lambda do end.class # Proc
這里 lambda {}
是一個整體,{ ... }
可以替換成do ... end
。
lambda 在函數編程語言中很熟悉了,在 Ruby 中我們也幾乎可以那么用。
通過調用 call 方法去執行這個“函數”。
q = lambda { |x| x * x }
q.call 3 # 9
更詳細的話這個帖子講的不錯:聊聊 Ruby 中的 block, proc 和 lambda。
4. Dynamic Dispatch
class Adef even xif x == 0trueelseputs "odd"odd(x - 1)endenddef odd xif x == 0falseelseputs "even"even(x - 1)endend
endclass B < Adef even xx % 2 == 0end
end
B 是 A 的子類,B 中重寫了 even 方法,也可以是使用 odd 方法,但是在調用 odd 方法的時候,odd 方法中使用的實際是 B 類中重寫的 even 方法。
b = B.new
b.odd 9
只會打印一個 even 字符串。
實際上 A 類中在調用方法的時候省略了 self ,實際上應該是 self.odd(x - 1)
self.even(x - 1)
。
那么實際去調用哪個方法,取決于 self 是什么,這里使用 B 的實例 b 去調用這個方法,self 就是 b 這個實例。
5. Multimethods
- Multiple dispatch
- What is the difference between multiple dispatch and method overloading?
- Overloading in Java and multiple dispatch
Multiple dispatch 中在運行時才決定具體去調用哪個同名函數,而 Method overloading 在編譯時已經確定了類型。
比方說 Java 多態的應用中,父類引用子類對象,在編譯期間確定類型就會導致不符合期望的調用。