呼叫方法¶ ↑
呼叫方法會將訊息傳送給物件,以便執行一些工作。
在 Ruby 中,您可以像這樣傳送訊息給物件
my_method()
請注意,括號是可選的
my_method
除了使用和省略括號之間有差異的情況外,本文檔在有參數時使用括號,以避免混淆。
此區段僅涵蓋呼叫方法。另請參閱定義方法的語法文件。
接收者¶ ↑
self
是預設接收者。如果您未指定任何接收者,將使用 self
。若要指定接收者,請使用 .
。
my_object.my_method
這會將 my_method
訊息傳送至 my_object
。任何物件都可以是接收者,但根據方法的可見性,傳送訊息可能會引發 NoMethodError
。
您也可以使用 ::
指定接收者,但由於與命名空間的 ::
容易混淆,因此很少使用。
串連方法呼叫¶ ↑
您可以透過緊接一個方法呼叫後再呼叫另一個方法來「串連」方法呼叫。
此範例串連方法 Array#append
和 Array#compact
a = [:foo, 'bar', 2] a1 = [:baz, nil, :bam, nil] a2 = a.append(*a1).compact a2 # => [:foo, "bar", 2, :baz, :bam]
詳細資料
-
第一個方法
merge
會建立a
的副本,將a1
的每個元素(個別)附加到副本,然後傳回[:foo, "bar", 2, :baz, nil, :bam, nil]
-
串連的方法
compact
會建立該傳回值的副本,移除其nil
值的項目,然後傳回[:foo, "bar", 2, :baz, :bam]
您可以串連不同類別中的方法。此範例串連方法 Hash#to_a
和 Array#reverse
h = {foo: 0, bar: 1, baz: 2} h.to_a.reverse # => [[:baz, 2], [:bar, 1], [:foo, 0]]
詳細資料
-
第一個方法
Hash#to_a
會將a
轉換為陣列,然後傳回[[:foo, 0], [:bar, 1], [:baz, 2]]
-
串連的方法
Array#reverse
會建立該傳回值的副本,將其反轉,然後傳回[[:baz, 2], [:bar, 1], [:foo, 0]]
安全導覽運算子¶ ↑
&.
,稱為「安全導覽運算子」,允許在接收者為 nil
時略過方法呼叫。如果略過呼叫,它會傳回 nil
,且不會評估方法的參數。
REGEX = /(ruby) is (\w+)/i "Ruby is awesome!".match(REGEX).values_at(1, 2) # => ["Ruby", "awesome"] "Python is fascinating!".match(REGEX).values_at(1, 2) # NoMethodError: undefined method `values_at' for nil:NilClass "Python is fascinating!".match(REGEX)&.values_at(1, 2) # => nil
這允許輕鬆串連可能會傳回空值的方法。請注意,&.
僅略過下一個呼叫,因此對於較長的串連,需要在每個層級新增運算子
"Python is fascinating!".match(REGEX)&.values_at(1, 2).join(' - ') # NoMethodError: undefined method `join' for nil:NilClass "Python is fascinating!".match(REGEX)&.values_at(1, 2)&.join(' - ') # => nil
參數¶ ↑
傳送訊息時有三種類型的參數,位置參數、關鍵字(或命名)參數和區塊參數。每個傳送的訊息都可以使用一種、兩種或所有類型的參數,但參數必須按此順序提供。
Ruby 中的所有參數都是按引用傳遞的,並且不會延遲評估。
每個參數都以 ,
分隔。
my_method(1, '2', :three)
參數可以是表達式、hash 參數
'key' => value
或關鍵字參數
key: value
Hash
和關鍵字參數必須是連續的,並且必須出現在所有位置參數之後,但可以混合使用
my_method('a' => 1, b: 2, 'c' => 3)
位置參數¶ ↑
訊息的位置參數跟在方法名稱之後
my_method(argument1, argument2)
在許多情況下,傳送訊息時不需要括號
my_method argument1, argument2
但是,為了避免歧義,括號是必要的。這將會引發 SyntaxError
,因為 Ruby 不知道應該將哪個方法 argument3 傳送給
method_one argument1, method_two argument2, argument3
如果方法定義有 *argument
,則額外的位置參數將會在方法中指定給 argument
作為 Array
。
如果方法定義不包含關鍵字參數,則關鍵字或 hash 類型的參數將指定為單一 hash 給最後一個參數
def my_method(options) p options end my_method('a' => 1, b: 2) # prints: {'a'=>1, :b=>2}
如果給定的位置參數太多,則會引發 ArgumentError
。
預設位置參數¶ ↑
當方法定義預設參數時,您不需要提供所有參數給方法。Ruby 會依序填入遺失的參數。
首先,我們將介紹預設參數出現在右邊的簡單案例。考慮這個方法
def my_method(a, b, c = 3, d = 4) p [a, b, c, d] end
在這裡,c
和 d
有 Ruby 會為您套用的預設值。如果您只傳送兩個參數給這個方法
my_method(1, 2)
您會看到 Ruby 印出 [1, 2, 3, 4]
。
如果您傳送三個參數
my_method(1, 2, 5)
您會看到 Ruby 印出 [1, 2, 5, 4]
Ruby 從左到右填入遺失的參數。
Ruby 允許預設值出現在位置參數的中間。考慮這個更複雜的方法
def my_method(a, b = 2, c = 3, d) p [a, b, c, d] end
在這裡,b
和 c
有預設值。如果您只傳送兩個參數給這個方法
my_method(1, 4)
您會看到 Ruby 印出 [1, 2, 3, 4]
。
如果您傳送三個參數
my_method(1, 5, 6)
您會看到 Ruby 印出 [1, 5, 3, 6]
。
用文字描述這一點很複雜且令人困惑。我將改用變數和值來描述。
首先,1
指定給 a
,然後 6
指定給 d
。這只留下具有預設值的參數。由於 5
尚未指定給值,因此它會給予 b
,而 c
使用其預設值 3
。
關鍵字參數¶ ↑
關鍵字參數遵循任何位置參數,並以逗號分隔,就像位置參數一樣
my_method(positional1, keyword1: value1, keyword2: value2)
任何未給定的關鍵字參數將使用來自方法定義的預設值。如果給定方法未列出的關鍵字參數,且方法定義不接受任意關鍵字參數,則會引發 ArgumentError
。
可以省略關鍵字參數值,表示將透過金鑰名稱從內容中擷取值
keyword1 = 'some value' my_method(positional1, keyword1:) # ...is the same as my_method(positional1, keyword1: keyword1)
請注意,當省略方法括號時,解析順序可能會出乎意料
my_method positional1, keyword1: some_other_expression # ...is actually parsed as my_method(positional1, keyword1: some_other_expression)
區塊參數¶ ↑
區塊參數會將封閉從呼叫範圍傳送至方法。
傳送訊息至方法時,區塊參數總是最後一個。使用 do ... end
或 { ... }
將區塊傳送至方法
my_method do # ... end
或
my_method { # ... }
do end
的優先權低於 { }
,因此
method_1 method_2 { # ... }
將區塊傳送至 method_2
,而
method_1 method_2 do # ... end
將區塊傳送至 method_1
。請注意,在第一個案例中,如果使用括號,則會將區塊傳送至 method_1
。
區塊會接受傳送至區塊的方法的參數。參數定義類似於方法定義參數的方式。區塊的參數會在開啟 do
或 {
之後,放入 | ... |
中
my_method do |argument1, argument2| # ... end
區塊區域參數¶ ↑
您也可以使用 ;
在區塊參數清單中宣告區塊區域參數。指派至區塊區域參數不會覆寫呼叫方範圍中區塊外部的區域參數
def my_method yield self end place = "world" my_method do |obj; place| place = "block" puts "hello #{obj} this is #{place}" end puts "place is: #{place}"
這會列印
hello main this is block place is world
因此,區塊中的 place
變數與區塊外的 place
變數不同。從區塊參數中移除 ; place
會產生以下結果
hello main this is block place is block
Array
轉換為參數¶ ↑
給定以下方法
def my_method(argument1, argument2, argument3) end
您可以使用 *
(或展開)運算子將 Array
轉換為參數清單
arguments = [1, 2, 3] my_method(*arguments)
或
arguments = [2, 3] my_method(1, *arguments)
兩者等同於
my_method(1, 2, 3)
您也可以使用 **
(在下一段中描述)將 Hash
轉換為關鍵字參數。
如果 Array
中的物件數量與方法的參數數量不符,將會引發 ArgumentError
。
如果展開運算子在呼叫中排在第一位,則必須使用括號來避免警告
my_method *arguments # warning my_method(*arguments) # no warning
Hash
轉換為關鍵字參數¶ ↑
給定以下方法
def my_method(first: 1, second: 2, third: 3) end
您可以使用 **
(關鍵字展開)運算子將 Hash
轉換為關鍵字參數
arguments = { first: 3, second: 4, third: 5 } my_method(**arguments)
或
arguments = { first: 3, second: 4 } my_method(third: 5, **arguments)
兩者等同於
my_method(first: 3, second: 4, third: 5)
如果方法定義使用關鍵字展開運算子來收集任意關鍵字參數,則不會由 *
收集
def my_method(*a, **kw) p arguments: a, keywords: kw end my_method(1, 2, '3' => 4, five: 6)
列印
{:arguments=>[1, 2], :keywords=>{'3'=>4, :five=>6}}
Proc
轉換為區塊¶ ↑
給定使用區塊的方法
def my_method yield self end
您可以使用 &
(區塊轉換)運算子將程序或 lambda 轉換為區塊參數
argument = proc { |a| puts "#{a.inspect} was yielded" } my_method(&argument)
如果區塊轉換運算子在呼叫中排在第一位,則必須使用括號來避免警告
my_method &argument # warning my_method(&argument) # no warning
Method
查詢¶ ↑
當您傳送訊息時,Ruby 會尋找與接收者的訊息名稱相符的方法。方法儲存在類別和模組中,因此方法查詢會遍歷這些類別和模組,而不是物件本身。
以下是接收者的類別或模組 R
的方法查詢順序
-
R
的預置模組,按相反順序排列 -
對於
R
中的匹配方法 -
R
的包含模組,按相反順序排列
如果 R
是具有超類別的類別,則會重複此步驟,使用 R
的超類別,直到找到方法。
一旦找到匹配,方法查詢就會停止。
如果找不到匹配,則會從頭開始重複此步驟,但尋找 method_missing
。預設的 method_missing
是 BasicObject#method_missing
,它在呼叫時會引發 NameError
。
如果改良(實驗性功能)處於啟用狀態,方法查詢就會變更。有關詳細資訊,請參閱 改良文件。