依型態資訊是記錄在宣告在變數之上,或者是執行時期的物件之上,程式語言可以區分為靜態(Static)語言(例如C/C++、Java)與動態(Dynamic)語言(例如Python、JavaScript)。
Ruby是動態語言,也就是變數本身並沒有型態資訊,型態的資訊是在執行時期的物件之上,在Ruby中有五種變數,包括區域變數、全域變數、實例變數、類別變數與常數。
要建立區域變數,無需宣告型態,只要以小寫字母開頭命名變數並指定值給它,就建立了一個變數,在建立變數之前,嘗試存取某個變數會發生變數未定義的錯誤。例如:
>> x
NameError: undefined local variable or method `x' for main:Object
from (irb):1
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>> x = 10
=> 10
>> x
=> 10
>> x = "Justin"
=> "Justin"
>> x
=> "Justin"
>>
NameError: undefined local variable or method `x' for main:Object
from (irb):1
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>> x = 10
=> 10
>> x
=> 10
>> x = "Justin"
=> "Justin"
>> x
=> "Justin"
>>
在上例中,一開始建立了一個x變數,參考至整數10,由於變數本身沒有型態,之後你可以將字串"Justin"指定給x。在Ruby中,變數始終是個參考至真實物件的名稱,指定運算只是更改變數的參考對象。例如:
>> x = 1.0
=> 1.0
>> y = x
=> 1.0
>> x.object_id
=> 3696828
>> y.object_id
=> 3696828
>> y = 2.0
=> 2.0
>> x.object_id
=> 3696828
>> y.object_id
=> 20871252
>>
=> 1.0
>> y = x
=> 1.0
>> x.object_id
=> 3696828
>> y.object_id
=> 3696828
>> y = 2.0
=> 2.0
>> x.object_id
=> 3696828
>> y.object_id
=> 20871252
>>
在上例中,x一開始參考至1.0浮點數物件,而後將x參考的物件指定給y來參考,你可以使用object_id方法來取得所參考物件的記憶體位址代表數字,可以看到x與y都參考同一物件。之後y參考至2.0,所以x與y就參考至不同的物件。
>> x = 1
=> 1
>> x.object_id
=> 3
>> x = x + 1
=> 2
>> x.object_id
=> 5
>>
=> 1
>> x.object_id
=> 3
>> x = x + 1
=> 2
>> x.object_id
=> 5
>>
一開始x參考至1整數物件,之後+運算後,建立了新的2整數物件,而後指定給x,所以x參考至新的物件。由於變數在Ruby中只是個參考至物件的名稱,所以對於可變動物件,才會有以下的操作結果:
>> x = [1, 2]
=> [1, 2]
>> x[0] = 10
=> 10
>> x
=> [10, 2]
>>
=> [1, 2]
>> x[0] = 10
=> 10
>> x
=> [10, 2]
>>
在Ruby中,==常單純比較兩個物件的實質內容是否相同。例如:
>> list1 = [1, 2, 3]
=> [1, 2, 3]
>> list2 = [1, 2, 3]
=> [1, 2, 3]
>> list1 == list2
=> true
>> list1.object_id
=> 21564936
>> list2.object_id
=> 21538056
>> list1.equal? list2
=> false
>>
=> [1, 2, 3]
>> list2 = [1, 2, 3]
=> [1, 2, 3]
>> list1 == list2
=> true
>> list1.object_id
=> 21564936
>> list2.object_id
=> 21538056
>> list1.equal? list2
=> false
>>
上例中,實際上list1與list2是參考至不同的物件,如果想知道兩個變數是否參考同一物件,除了使用object_id得知之外,通常還可以使用equal?方法。
相等比較還可以使用eql?方法,這個方法通常會檢查變數是否參考同一實例,若否則比較物件是否為同一類別的實例,若是則比較實值是否相同。例如:
>> x = 1.0
=> 1.0
>> y = 1
=> 1
>> x == y
=> true
>> x.eql? y
=> false
>>
=> 1.0
>> y = 1
=> 1
>> x == y
=> true
>> x.eql? y
=> false
>>
上例中,x與y參考的物件分別是Float與Fixnum的實例,因此eql?比較結果為false。
相等比較還可以使用===,通常若===兩邊都是實例,預設實作會比較兩個變數是否參考同一實例,如果不是,會再呼叫==。如果左邊是類別而右且是實例,===比較實例是否由該類別所生成。使用case...when...else 時,就是使用===作為依據。
Ruby區域變數有個特性,直譯器只要看到程式碼中有「變數=值」的語句,就會為建立該變數,這會造成有以下的結果:
>> if false
>> a = 10
>> end
=> nil
>> a
=> nil
>> b
NameError: undefined local variable or method `b' for main:Object
from (irb):5
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>
>> a = 10
>> end
=> nil
>> a
=> nil
>> b
NameError: undefined local variable or method `b' for main:Object
from (irb):5
from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>
在上例中,雖然if的區塊不會執行,但直譯器看到a = 10,就會建立a變數,因此之後嘗試顯示a的值,是有a變數但預設為nil,然後b變數是不存在的,因此出現錯誤。
在Ruby中若要建立全域變數,變數名稱只要以$開頭,全域變數一旦建立,整個程式都是可見。例如:
- main.rb
$x = 10
x = 20
puts "%d, %d" % [$x, x]
load "util.rb"
puts "%d, %d" % [$x, x]
- util.rb
$x = 100
x = 200
在上例中,main.rb中的$x是全域變數,x是區域變數,範圍限於main.rb中,util.rb中的$x是全域變數,x是區域變數,範圍限於util.rb中,在load "util.rb"執行其內容時,$x被設定為100,由於是全域變數,這個修改在main.rb是可見的,因此第二次puts時,會看到$x的值為100。
>ruby main.rb
10, 20
100, 20
>
10, 20
100, 20
>
在Ruby中有一些內建的全域變數,用來儲存整個系統可見的資訊,可查看Ruby安裝目錄中的English.rb,瞭解有哪些全域變數可用。例如:
>> $$
=> 4656
>> require "English"
=> true
>> $PID
=> 4656
>>
=> 4656
>> require "English"
=> true
>> $PID
=> 4656
>>
$$可以取得目前程式的行程ID,要求English特性之後,可以用較清楚的名稱使用全域變數,例如$PID就等於$$。
如果變數名稱以大寫作開頭,則稱之為常數,在Ruby中,常數並非不能修改,只不過你對常數作第二次修改的話,直譯器會提出警告:
>> XCONST = 10
=> 10
>> XCONST = 20
(irb):2: warning: already initialized constant XCONST
=> 20
>>
=> 10
>> XCONST = 20
(irb):2: warning: already initialized constant XCONST
=> 20
>>
實例變數與類別變數,之後會再說明。