變數範圍


變數 中談過,在Ruby中有五種變數,包括區域變數全域變數實例變數類別變數常數,目前已經談過的有區塊變數、全域變數與常數,在看過 def 定義方法迭代器與程式區塊 後,這邊要來談談區塊變數、全域變數與常數的範圍問題。

全域變數比較不需要討論,全域變數的範圍為整個程式可見,比較要討論的是區塊變數,在Ruby中,區域變數的可見範圍真的就是「區域」,變數在哪個範圍中宣告,就在哪個範圍中可見。例如:
>> x = 10
=> 10
>> def some
>>     puts x
>> end
=> nil
>> some

NameError: undefined local variable or method `x' for main:Object
        from (irb):3:in `some'
        from (irb):5
        from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>


上例於some方法外定義了x區域變數(實際上是Object類別本體中的區域變數,之後還會談到)some方法中是看不到該變數的。類似地,
>> def some
>>     x = 10
>>     def inner
>>         puts x
>>     end
>>     inner
>> end
=> nil
>> some
NameError: undefined local variable or method `x' for main:Object
        from (irb):15:in `inner'
        from (irb):17:in `some'
        from (irb):19
        from C:/Winware/Ruby192/bin/irb:12:in `<main>'
>>


some方法中宣告了x區域變數,inner是看不到該變數的。要小心以下:
>> x = 10
=> 10
>> def some
>>     x = 20
>>     puts x
>> end
=> nil
>> some
20
=> nil
>> x
=> 10
>>


上例中some中的x指定,其實是建立了新的區域變數,而不是將值指定給some方法外的x變數

變數 中談過,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>'
>>


在上例中,雖然if的區塊不會執行,但直譯器看到a = 10,就會建立a變數,因此之後嘗試顯示a的值,是有a變數但預設為nil,然後b變數是不存在的,因此出現錯誤。

如果在方法外定義常數的話,方法內是可見的:
>> X = 10
=> 10
>> def some
>>     puts X
>> end
=> nil
>> some
10
=> nil
>>


如果使用程式區塊,程式區塊外的區域變數,程式區塊中是看得到的:
>> sum = 0
=> 0
>> [1, 2, 3].each do |element|
?>     sum += element
>> end
=> [1, 2, 3]
>> sum
=> 6
>>


如果怕區塊中使用的變數,剛好與區塊外的變數同名,可以特別在分號之後宣告,告知區域中這個變數必須是區域變數:
>> x = 10
=> 10
>> [1, 2, 3].each do |element; x|
?>     x = element + 1
>> end
=> [1, 2, 3]
>> x
=> 10
>>


定義程式區塊時,若區塊參數湊巧與區塊外的區域變數同名,則只會看到區塊參數:
>> sum = 0
=> 0
>> [1, 2, 3, 4, 5].reduce { |sum, element|
?>     sum + element
>> }
=> 15
>> sum
=> 0
>>


想得知變數是哪個範圍的變數,可以使用defined?方法:
>> x = 10
=> 10
>> X = 10
=> 10
>> defined? x
=> "local-variable"
>> defined? X
=> "constant"
>> defined? xyz
=> nil
>>