Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

344 lines (253 sloc) 12.279 kb

當你第一眼看見 Ruby 程式碼,你一定會想起你熟悉的程式語言。這正是本文件的目的。Ruby 有許多語法和 Perl、Python 和 Java (以及其他程式語言) 類似,所以如果你已經熟悉這些程式語言,那麼學習 Ruby 易如反掌。

這份文件包括兩大部份。這一部分的用意是整理從 X 語言到 Ruby 的重點。第二部分則從 Ruby 的重要功能及特色著手,與其他程式語言來做比較。

重點整理: 從 程式語言 X 到 Ruby

重要的語言特色及一些訣竅

這裡是你學習 Ruby 的一些重點及提示。

迭代 (Iteration)

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

Symbols 不是輕量化的字串

許多 Ruby 新手會搞不清楚什麼是 Symbols(符號) 可以做什麼用。

Symbols 就如同一個識別符號。一個 symbol 就代表它是"誰"了,而不是代表它是"什麼"。打開 irb 來看一看它們的區別:

irb(main):001:0> :george.object_id == :george.object_id => true irb(main):002:0> "george".object_id == "george".object_id => false irb(main):003:0>

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}

一切為 true

在 Ruby 裡,除了 nilfalse 之外的所有東西,都可以當做 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 的:

class MyClass # 這個 a_method 是 public 的 def a_method; true; end private # 這個 another_method 是 private 的 def another_method; false; end end

public, privateprotected 其實也是一種方法,所以可以接受參數。如果你傳入一個 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 去,例如:

class Fixnum def hours self * 3600 # 一小時有多少秒 end alias hour hours end # 從一月一號 00:00 往後數 14 個小時 # (你終於醒了吧 ;) Time.mktime(2006, 01, 01) + 14.hours # => Sun Jan 01 14:00:00

有趣的方法名稱

在 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

Missing 方法

當你呼叫一個不存在的方法,Ruby 仍然有辦法處理。它會改呼叫 method_missing 這個方法,並把這個不存在的方法名稱傳進去當做參數。method_missing 預設會丟出一個 NameError 例外,但是你可以根據你的需求重新定義過,也有許多函式庫這麼做。這是一個例子:

# id 是被呼叫方法的名字,而 * 符號會收集 # 所有傳進來的參數變成一個叫做 'arguments' 的陣列 def method_missing( id, *arguments ) puts "Method #{id} was called, but not found. It has " + "these arguments: #{arguments.join(", ")}" end __ :a, :b, 10 # => Method __ was called, but not found. It has these # arguments: a, b, 10

以上程式會輸出呼叫的細節,但你可以隨意定義這個訊息。

傳遞訊息,不是呼叫函數

一個方法呼叫(method call)其實就是送一個 訊息(message) 給一個物件:

# 這個 1 + 2 # 等同於 ... 1.+(2) # 也等同於: 1.send "+", 2

Blocks 也算是物件

程式區塊 Blocks (或叫做 closures) 被廣泛應用在標準函式庫。要執行一個程式區塊,可以用 yield ,或是透過一個特別的參數讓它變成 @Proc@,例如:

def block( &the_block ) # 在這裡面,the_block 是被傳進來的程式區塊 the_block # return the block end adder = block { |a, b| a + b } # adder 是一個 Proc 物件 adder.class # => 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_varvar = var * other_var 等的縮寫,因此也不能被覆寫定義。

更多資料

如果你需要更多 Ruby 知識,請參考 文件

Jump to Line
Something went wrong with that request. Please try again.