指派¶ ↑
在 Ruby 中,指派使用 =
(等號)字元。此範例將數字 5 指派給區域變數 v
v = 5
如果先前未參考過變數,指派會建立區域變數。
指派表達式的結果永遠都是指派的值,包括 指派方法。
局部變數名稱¶ ↑
局部變數名稱必須以小寫的 US-ASCII 字母或設定了第八位的字元開頭。通常局部變數相容於 US-ASCII,因為所有鍵盤都有輸入這些字元的按鍵。
(Ruby 程式必須以相容於 US-ASCII 的字元集撰寫。在這些字元集中,如果設定了第八位,表示一個延伸字元。Ruby 允許局部變數包含這些字元。)
局部變數名稱可以包含字母、數字、_
(底線或低線)或設定了第八位的字元。
局部變數範圍¶ ↑
一旦指派給局部變數名稱,在範圍剩餘部分中所有使用該名稱的變數都會被視為局部變數。
以下是一個範例
1.times do a = 1 puts "local variables in the block: #{local_variables.join ", "}" end puts "no local variables outside the block" if local_variables.empty?
這會列印
local variables in the block: a no local variables outside the block
由於區塊建立了一個新的範圍,因此在其中建立的任何局部變數都不會外洩到周圍的範圍。
在外層範圍中定義的變數會顯示在內層範圍中
a = 0 1.times do puts "local variables: #{local_variables.join ", "}" end
這會列印
local variables: a
您可以在區塊中列出變數,並在區塊引數中列出分號 (;
) 後面的變數,以將區塊中的變數與外層範圍隔離。請參閱 呼叫方法 文件中的區塊局部變數文件,以取得範例。
另請參閱 Kernel#local_variables
,但請注意 for
迴圈不會像區塊一樣建立新的範圍。
局部變數和方法¶ ↑
在 Ruby 中,局部變數名稱和方法名稱幾乎相同。如果您尚未指派給其中一個有歧義的名稱,Ruby 會假設您想要呼叫方法。一旦您指派給名稱,Ruby 會假設您想要參照局部變數。
當解析器遇到指派時會建立局部變數,而不是在指派發生時
a = 0 if false # does not assign to a p local_variables # prints [:a] p a # prints nil
方法和局部變數名稱的相似性可能會導致混淆的程式碼,例如
def big_calculation 42 # pretend this takes a long time end big_calculation = big_calculation()
現在,任何參照 big_calculation
的內容都會被視為局部變數,並會被快取。若要呼叫方法,請使用 self.big_calculation
。
您可以使用如上所示的空引數括弧或使用明確的接收器(例如 self
)來強制呼叫方法。如果方法的可見性不是公開的,或接收器是文字 self
,使用明確的接收器可能會引發 NameError
。
另一個常見的混淆情況是使用修改詞 if
時
p a if a = 0.zero?
您會收到 NameError
,而不是印出「true」,「未定義的區域變數或方法『a』」。由於 Ruby 會先剖析 if
左側的裸 a
,且尚未看到指派給 a
的值,因此它假設您想要呼叫一個方法。然後 Ruby 會看到指派給 a
的值,並假設您正在參照一個區域變數。
混淆來自於表達式的無序執行。首先指派給區域變數,然後您嘗試呼叫一個不存在的方法。
區域變數和 eval¶ ↑
使用 eval
來評估 Ruby 程式碼,將允許存取在相同範圍內定義的區域變數,即使區域變數是在呼叫 eval
之後才定義的。然而,在呼叫 eval
內部定義的區域變數,將不會反映在周圍的範圍內。在呼叫 eval
內部,在周圍的範圍內定義的區域變數和在呼叫 eval
內部定義的區域變數,將會是可以存取的。然而,您將無法存取在相同範圍內之前或之後呼叫 eval
所定義的區域變數。將每個 eval
呼叫視為一個獨立的巢狀範圍。範例
def m eval "bar = 1" lvs = eval "baz = 2; ary = [local_variables, foo, baz]; x = 2; ary" eval "quux = 3" foo = 1 lvs << local_variables end m # => [[:baz, :ary, :x, :lvs, :foo], nil, 2, [:lvs, :foo]]
實例變數¶ ↑
實例變數會在同一個物件的所有方法中共享。
一個實例變數必須以 @
(「at」符號或商業符號)開頭。否則實例變數名稱遵循與區域變數名稱相同的規則。由於實例變數以 @
開頭,因此第二個字元可以是大寫字母。
以下是實例變數使用方式的範例
class C def initialize(value) @instance_variable = value end def value @instance_variable end end object1 = C.new "some value" object2 = C.new "other value" p object1.value # prints "some value" p object2.value # prints "other value"
一個未初始化的實例變數具有 nil
的值。如果您在啟用警告的情況下執行 Ruby,則在存取未初始化的實例變數時,您將會收到警告。
value
方法可以存取由 initialize
方法設定的值,但僅限於同一個物件。
Class
變數¶ ↑
Class
變數會在一個類別、其子類別及其實例之間共享。
一個類別變數必須以 @@
(兩個「at」符號)開頭。名稱的其餘部分遵循與實例變數相同的規則。
以下是一個範例
class A @@class_variable = 0 def value @@class_variable end def update @@class_variable = @@class_variable + 1 end end class B < A def update @@class_variable = @@class_variable + 2 end end a = A.new b = B.new puts "A value: #{a.value}" puts "B value: #{b.value}"
這會列印
A value: 0 B value: 0
繼續使用相同的範例,我們可以使用來自任一類別的物件進行更新,且值會被共享
puts "update A" a.update puts "A value: #{a.value}" puts "B value: #{b.value}" puts "update B" b.update puts "A value: #{a.value}" puts "B value: #{b.value}" puts "update A" a.update puts "A value: #{a.value}" puts "B value: #{b.value}"
這會列印
update A A value: 1 B value: 1 update B A value: 3 B value: 3 update A A value: 4 B value: 4
存取未初始化的類別變數會引發 NameError
例外。
請注意,類別有實例變數,因為類別是物件,所以請盡量不要混淆類別和實例變數。
全域變數¶ ↑
全域變數在任何地方都可以存取。
全域變數以 $
(美元符號)開頭。名稱的其餘部分遵循與實例變數相同的規則。
以下是一個範例
$global = 0 class C puts "in a class: #{$global}" def my_method puts "in a method: #{$global}" $global = $global + 1 $other_global = 3 end end C.new.my_method puts "at top-level, $global: #{$global}, $other_global: #{$other_global}"
這會列印
in a class: 0 in a method: 0 at top-level, $global: 1, $other_global: 3
未初始化的全域變數的值為 nil
。
Ruby 有些特殊全域變數,會根據不同的情況表現出不同的行為,例如正規表達式比對變數,或是在指定時會產生副作用。有關詳細資訊,請參閱 全域變數文件。
指定方法¶ ↑
您可以定義會像指定一樣運作的方法,例如
class C def value=(value) @value = value end end c = C.new c.value = 42
使用指定方法可以讓您的程式看起來更漂亮。在指定到實例變數時,大多數人會使用 Module#attr_accessor
class C attr_accessor :value end
在使用方法指定時,您必須始終有一個接收器。如果您沒有接收器,Ruby 會假設您正在指定到一個區域變數
class C attr_accessor :value def my_method value = 42 puts "local_variables: #{local_variables.join ", "}" puts "@value: #{@value.inspect}" end end C.new.my_method
這會列印
local_variables: value @value: nil
若要使用指定方法,您必須設定接收器
class C attr_accessor :value def my_method self.value = 42 puts "local_variables: #{local_variables.join ", "}" puts "@value: #{@value.inspect}" end end C.new.my_method
這會列印
local_variables: @value: 42
請注意,指定方法傳回的值會被忽略,因為指定表達式的結果永遠都是指定值。
縮寫指定¶ ↑
您可以混合多個運算子與指定。若要將 1 加到一個物件,您可以寫
a = 1 a += 2 p a # prints 3
這等於
a = 1 a = a + 2 p a # prints 3
您可以這樣使用下列運算子:+
、-
、*
、/
、%
、**
、&
、|
、^
、<<
、>>
還有 ||=
和 &&=
。前者會在值為 nil
或 false
時進行指定,而後者會在值不是 nil
或 false
時進行指定。
以下是一個範例
a ||= 0 a &&= 1 p a # prints 1
請注意,這兩個運算子的行為更像是 a || a = 0
,而不是 a = a || 0
。
隱含 Array
指定¶ ↑
您可以在指定時列出多個值,以隱含建立一個陣列
a = 1, 2, 3 p a # prints [1, 2, 3]
這會隱含建立一個 Array
。
您可以在指定時使用 *
或「展開」運算子,或解開一個 Array
。這類似於多重指定
a = *[1, 2, 3] p a # prints [1, 2, 3] b = *1 p b # prints [1]
您可以在指定右側的任何地方展開
a = 1, *[2, 3] p a # prints [1, 2, 3]
多重指定¶ ↑
您可以在右側將多個值指定到多個變數
a, b = 1, 2 p a: a, b: b # prints {:a=>1, :b=>2}
在以下各節中,任何地方使用「變數」時,指定方法、實例、類別或全域變數也會運作
def value=(value) p assigned: value end self.value, $global = 1, 2 # prints {:assigned=>1} p $global # prints 2
您可以在原地使用多重賦值來交換兩個值
old_value = 1 new_value, old_value = old_value, 2 p new_value: new_value, old_value: old_value # prints {:new_value=>1, :old_value=>2}
如果賦值運算的右手邊值比左手邊的變數多,多餘的值會被忽略
a, b = 1, 2, 3 p a: a, b: b # prints {:a=>1, :b=>2}
您可以在賦值運算的右手邊使用 *
來收集多餘的值。
a, *b = 1, 2, 3 p a: a, b: b # prints {:a=>1, :b=>[2, 3]}
*
可以出現在左手邊的任何位置
*a, b = 1, 2, 3 p a: a, b: b # prints {:a=>[1, 2], :b=>3}
但是您只能在賦值中使用一個 *
。
陣列
分解¶ ↑
就像 陣列
在 方法參數 中的分解,您可以在賦值期間使用括號分解 陣列
(a, b) = [1, 2] p a: a, b: b # prints {:a=>1, :b=>2}
您可以將 陣列
分解為較大的多重賦值的一部分
a, (b, c) = 1, [2, 3] p a: a, b: b, c: c # prints {:a=>1, :b=>2, :c=>3}
由於每個分解都被視為其自己的多重賦值,因此您可以在分解中使用 *
來收集參數
a, (b, *c), *d = 1, [2, 3, 4], 5, 6 p a: a, b: b, c: c, d: d # prints {:a=>1, :b=>2, :c=>[3, 4], :d=>[5, 6]}