特殊方法定義


在Ruby中可以定義特定操作或運算操作的行為,例如initialize可以定義建立實例之後初始化的流程,+、-、*、/==等操作行為也可以使用方法定義,例如自行定義一個有理數類別:
# encoding: Big5
class RationalNumber
attr_accessor :numer, :denom
def initialize(n, d) # 物件建立之後所要建立的初始化動作
@numer = n
@denom = d
end

def to_s # 定義物件的字串描述
"#{@numer}/#{@denom}"
end

def +(that) # 定義 + 運算
RationalNumber.new(self.numer * that.denom + that.numer * self.denom,
self.denom * that.denom)
end

def -(that) # 定義 - 運算
RationalNumber.new(self.numer * that.denom - that.numer * self.denom,
self.denom * that.denom)
end

def *(that) # 定義 * 運算
RationalNumber.new(self.numer * that.numer,
self.denom * that.denom)
end

def /(that) # 定義 / 運算
RationalNumber.new(self.numer * that.denom,
self.denom * that.denom)
end

def ==(that) # 定義 == 運算
self.numer * that.denom == that.numer * self.denom
end
end

x = RationalNumber.new(1, 2)
y = RationalNumber.new(2, 3)
z = RationalNumber.new(2, 3)

puts x # 1/2
puts y # 2/3
puts x + y # 7/6
puts x - y # -1/6
puts x * y # 2/6
puts x / y # 3/6
puts x == y # false
puts y == z # true

initialize定義物件建立後要執行的初始化過程。常見的+、-、*、/、==等操作,可分別由+、-、*、/、==等方法定義,呼叫這些方法時,可以不用.操作,而呼叫方法有Ruby中,括號可以視情況省略,因此看來就像是其它語言中的所謂的運算子。

self代表(參考)至訊息接收者,實例方法中撰寫self時,self代表(參考)至實例,也就是運算操作左邊的物件。

to_s用來定義傳回物件描述字串,通常用來描述的字串是對使用者友善的說明文字,有些方法會對物件呼叫to_s來取得物件的字串描述,像是puts、print、p等方法(
irb中也是使用to_s取得字串描述),如果雙引號字串中包括\\忽略Escape字元,puts與print會忽略下一個字元,而p則不會忽略直接輸出。

與to_s類似的是to_str方法,在運算操作時(例如串接字串)如果需要從物件取得字串,若沒有指定方法操作,則會呼叫to_str而不是to_s。例如:
>> class Some
>>     def to_s
>>         "Some_to_s"
>>     end
>>     def to_str
>>         "Some_to_str"
>>     end
>> end
=> nil
>> s = Some.new
=> Some_to_s
>> "Text" + s
=> "TextSome_to_str"
>>


上例中同時定義了to_s與to_str,可以清楚看到irb中使用的是to_s,而串接字串時會使用to_str。

在某些操作場合,需要從物件取得陣列(例如串接陣列),若沒有指定方法操作,則通常預設會呼叫to_ary。例如:

>> class Some
>>     def to_ary
>>         ["S", "o", "m", "e"]
>>     end
>> end
=> nil
>> s = Some.new
=> #<Some:0x258c018>
>> [1, 2, 3] + s
=> [1, 2, 3, "S", "o", "m", "e"]
>>


實例變數的設值方法,可以使用name=來定義,其中name為實例變數名稱。類似地,[]運算操作的行為,可用[]與[]=方法來定義。例如:
>> class Some
>>     def initialize
>>         @inner = {}
>>     end
>>     def []=(name, value)
>>         @inner[name] = value
>>     end
>>     def [](name)
>>         @inner[name]
>>     end
>> end
=> nil
>> s = Some.new
=> #<Some:0x2a02820 @inner={}>
>> s[0] = 100
=> 100
>> s["Justin"] = "Message"
=> "Message"
>> s[0]
=> 100
>> s["Justin"]
=> "Message"
>>


單元運算的方法名稱比較特殊,為運算字元後加上@。例如:
>> class Some
>>     attr_accessor :value
>>     def initialize(value)
>>         @value = value
>>     end
>>     def -@
>>         Some.new(-value)
>>     end
>> end
=> nil
>> s = Some.new(10)
=> #<Some:0x26a1e90 @value=10>
>> s.value
=> 10
>> s2 = -s
=> #<Some:0x24ce568 @value=-10>
>> s2.value
=> -10
>>

要注意,=不能 使用方法定義,所以其它如+=、-=...等也不能使用方法定義,&&與||具有捷徑運算,你也無法用方法定義,因此& &=與||=也無法使用方法定義。可以使用方法定義的運算操作有+、-、*、/、%、[]、[]=、<<、>>、==、 >、<、>=、<=、===、&、|、^、~、!。

迭代器與程式區塊 中談過,可以為物件定義迭代器,如果某個物件上擁有each迭代方法,也就可以使用for語法。例如陣列就擁有each方法,可以使用each方法迭代元素,也可以使用for迭代元素
>> [1, 2, 3].each do |element|
?>     print element
>> end
123=> [1, 2, 3]
>> for element in [1, 2, 3]
>>     print element
>> end
123=> [1, 2, 3]
>>