指派¶ ↑
在 Ruby 中,指派使用 =
(等號)字元。此範例將數字五指派給區域變數 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」,「未定義的 local 變數或方法『a』」。由於 Ruby 會先解析 if
左邊的 a
,且尚未看到對 a
的指定,因此它假設您想要呼叫一個方法。然後,Ruby 會看到對 a
的指定,並假設您正在參照一個 local 變數。
混淆來自於表達式的無序執行。首先,指定 local 變數,然後您嘗試呼叫一個不存在的方法。
Local 變數和 eval¶ ↑
使用 eval
來評估 Ruby 程式碼,將允許存取在相同範圍內定義的 local 變數,即使 local 變數是在呼叫 eval
之後才定義的。然而,在呼叫 eval
內部定義的 local 變數,不會反映在周圍的範圍內。在呼叫 eval
內部,在周圍的範圍內定義的 local 變數和在呼叫 eval
內部定義的 local 變數,都可以存取。然而,您將無法存取在相同範圍內先前或後續呼叫 eval
中定義的 local 變數。將每個 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」符號或商業 at)開頭。否則,實例變數名稱遵循與 local 變數名稱相同的規則。由於實例變數以 @
開頭,因此第二個字元可以是大寫字母。
以下是實例變數使用範例
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]
您可以在指定作業的右方任何地方使用 splat
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]}