當你第一眼看見 Ruby 程式碼,你一定會想起你熟悉的程式語言。這正是本文件的目的。Ruby 有許多語法和 Perl、Python 和 Java (以及其他程式語言) 類似,所以如果你已經熟悉這些程式語言,那麼學習 Ruby 易如反掌。
這份文件包括兩大部份。這一部分的用意是整理從 X 語言到 Ruby 的重點。第二部分則從 Ruby 的重要功能及特色著手,與其他程式語言來做比較。
這裡是你學習 Ruby 的一些重點及提示。
Ruby 有兩個常用的特色你可能沒見過,那就是 “程式區塊(blocks)” 和迭代子(iterators)"。不像使用索引的迴圈(例如 C, C++ 和 pre-1.5 Java),或是迴圈控制結構(例如 Perl 的 for (@a) {…},或是 Python 的 for i in aList: …)。在 Ruby 裡你會常常看到:
some_list.each do |this_item| # 我們在程式區塊中 # 處理 this_item end關於更多 each
的資訊 (以及 collect
, find
, inject
, sort
等等),請參考 ri Enumerable
(和 ri Enumerable#func_name).
表達式(expression)和敘述(statement)沒有差別,都會有回傳值,即使那個值是 *nil*。例如下述用法:
x = 10 y = 11 z = if x < y true else false end z # => true許多 Ruby 新手會搞不清楚什麼是 Symbols(符號) 可以做什麼用。
Symbols 就如同一個識別符號。一個 symbol 就代表它是"誰"了,而不是代表它是"什麼"。打開 irb
來看一看它們的區別:
object_id
方法會回傳物件的識別編號。如果有兩個物件有相同的 object_id
表示它們其實是同一個(指向同一個記憶體位置)。
如你所見,使用過 Symbols 之後,任何相同名字的 Symbol 都是指記憶體裡的同一個物件。對任何相同名字的 Symbols,它們的 object_id
都一樣。
讓我們來看看字串 "george",它們的 object_id
並不相同。這表示它們在記憶體裡面是不同的物件。每次你建立一個新的字串,Ruby 就會分配新的記憶體空間給它。
如果你不清楚何時使用 Symbol 何時用字串(String),想想看用途究竟是物件的識別(例如一個雜湊 Hash 的 key),還是物件內容(比如這個例子的 “george”)。
“所有東西都是物件” 並不是誇大,甚至是類別跟整數也是物件,你可以與其他物件一樣操作它們:
# 這是等價的程式: # class MyClass # attr_accessor :instance_var # end MyClass = Class.new do attr_accessor :instance_var end譯註:在 Ruby 中任何類別都是由 Class
類別所實例(new)出來的物件。
常數(Constant)並不真的無法改變。如果你修改了一個已經有值的常數,你會得到一個警告訊息,但程式不會終止。當然這不表示你"應該"修改常數的值。
Ruby 規定了一些命名慣例。變數的識別名稱,大寫字母開頭的是常數、錢號($)開頭的是全域變數、@
開頭是實例變數(instance variable)、@@
開頭則是類別變數。
方法名稱可以允許大寫字母開頭,雖然可能造成一些混淆,例如:
Constant = 10 def Constant 11 end這裡的 Constant
是 10,但是 Constant()
卻是 11。
Ruby 不像 Python 有關鍵字參數(keyword parameters)功能,但是可以用 symbols 和雜湊(hash) 來替代。Ruby on Rails 和非常多的函式庫都使用了這個方法,例如:
def some_keyword_params( params ) params end some_keyword_params( :param_one => 10, :param_two => 42 ) # => {:param_one=>10, :param_two=>42}在 Ruby 裡,除了 nil 和 false 之外的所有東西,都可以當做 true 值。在 C, Python 和其他語言中,0 和一些其他值,例如空列表,會被當做 false。例如我們看看以下的 Python 程式(其他語言亦同):
# in Python if 0: print "0 is true" else: print "0 is false"這會輸出 “0 is false”。而在 Ruby 裡:
# in Ruby if 0 puts "0 is true" else puts "0 is false" end這會輸出 “0 is true”。
在下面的 Ruby 程式中,
class MyClass private def a_method; true; end def another_method; false; end end你可能會認為 another_method
是 public 的,但不是這樣。這個 ‘private’ 存取修飾到作用域(scope)結束,或是直到另一個存取修飾詞開始作用。方法預設都是 public 的:
public
, private
和 protected
其實也是一種方法,所以可以接受參數。如果你傳入一個 Symbol,那個該 Symbol 代表的方法就會改變存取權限。
在 Java 裡,public
表示方法可以被任何人呼叫。protected
表示只有這個類別的實例、衍生類別的實例,以及相同 package 類別的實例可以呼叫,而 private
表示除了這個類別的實例之外,其他都不行呼叫。
在 Ruby 中,public
還是一樣是公開的意思,其他則有一點差異。private
表示只有不指定接受者(receiver)時才可以呼叫,也就是只有 self 可以當成 private 方法的接受者。
protected
也有點不同。一個 protected 方法除了可以被一個類別或衍生類別的實例呼叫,也可以讓另一個相同類別的實例來當做接受者。
來看看 Ruby FAQ 的例子:
$ irb irb(main):001:0> class Test irb(main):002:1> # 預設是 public 的 irb(main):003:1* def func irb(main):004:2> 99 irb(main):005:2> end irb(main):006:1> irb(main):007:1* def ==(other) irb(main):008:2> func == other.func irb(main):009:2> end irb(main):010:1> end => nil irb(main):011:0> irb(main):012:0* t1 = Test.new => # irb(main):013:0> t2 = Test.new => # irb(main):014:0> t1 == t2 => true irb(main):015:0> # 來讓 `func` 變成 protected,一樣沒問題 irb(main):016:0* # 因為 protected 允許其他相同類別的實例呼叫 irb(main):017:0* class Test irb(main):018:1> protected :func irb(main):019:1> end => Test irb(main):020:0> t1 == t2 => true irb(main):021:0> # 來讓 `func` 變成 private irb(main):022:0* class Test irb(main):023:1> private :func irb(main):024:1> end => Test irb(main):025:0> t1 == t2 NoMethodError: private method `func' called for # from (irb):8:in `==' from (irb):25 from :0 irb(main):026:0>Ruby 的類別是開放的,你可以隨時打開它新增一點程式或是修改。即使是核心類別如 Fixnum
或是 Object
(這是所有類別的父類別) 都一樣。 Ruby on Rails 甚至定義了一堆時間方法到 Fixnum
去,例如:
在 Ruby 裡,方法名稱允許用問號或驚嘆號結尾。慣例上,用來回答是非題的方法會用問號結尾 (例如 Array#empty? 會回傳 true 如果方法接收者是空的)。有潛在 “危險” (表示有某種副作用,會修改 self 或參數值。例如 exit!
等) 的方法會用驚嘆號結尾。
但是這不表示所有會修改參數的方法一定有驚嘆號結尾,例如 Array#replace 就會替換內容成別的陣列,畢竟 replace 的意思就是要修改替換自己。
單件方法(Singleton methods)是個別物件才有的方法。它們只存在於你要定義的物件之中。
class Car def inspect "Cheap car" end end porsche = Car.new porsche.inspect # => Cheap car def porsche.inspect "Expensive car" end porsche.inspect # => Expensive car # 其他物件就不受影響 other_car = Car.new other_car.inspect # => Cheap car當你呼叫一個不存在的方法,Ruby 仍然有辦法處理。它會改呼叫 method_missing
這個方法,並把這個不存在的方法名稱傳進去當做參數。method_missing 預設會丟出一個 NameError 例外,但是你可以根據你的需求重新定義過,也有許多函式庫這麼做。這是一個例子:
以上程式會輸出呼叫的細節,但你可以隨意定義這個訊息。
一個方法呼叫(method call)其實就是送一個 訊息(message) 給一個物件:
# 這個 1 + 2 # 等同於 ... 1.+(2) # 也等同於: 1.send "+", 2程式區塊 Blocks (或叫做 closures) 被廣泛應用在標準函式庫。要執行一個程式區塊,可以用 yield
,或是透過一個特別的參數讓它變成 @Proc@,例如:
你也可以透過 Proc.new 或 lambda 在方法外建立程式區塊。
同樣的,方法也可以當做物件:
method(:puts).call "puts is an object!" # => puts is an object!大部分的 Ruby 操作符(operators)只是一種方法呼叫的語法包裝(syntactic sugar),加上一些優先權規則。你要的話,舉例來說,我們可以覆寫掉 Fixnum 的 + 方法:
class Fixnum # 可以這麼做,但請不要這麼改 def +( other ) self - other end end你不需要 C++ 的 operator+
等等。
甚至有如陣列存取的 []
和 []=
可以定義。要定義一元的 + and – (想想看 1 跟 -2),你必須分別定義
方法。</code> 和 <code>-
以下的操作符則*不是*語法包裝。它們不是方法,不能被覆寫定義:
=, .., ..., !, not, &&, and, ||, or, !=, !~, ::此外 +=, *= 等只是 var = var + other_var
跟 var = var * other_var
等的縮寫,因此也不能被覆寫定義。
如果你需要更多 Ruby 知識,請參考 文件 。