Ruby有三類變量,一種常量和兩種嚴格意義上的偽變量(pseudo-variables).變量和常量都沒有類型.雖然無類型變量存在一定的缺點,但卻有更多的優點并很好的符合Ruby快速簡便(quick?and?easy)的哲學精神.
在大多數語言里,變量都必須指定其類型,可更改性(是不是個常數)和范圍;由于類型的不存在,剩下的東西也可由變量名字很快確定(你馬上會看見),在Ruby里我們不需要變量聲明.
由首字母標識符將其分類:
$??????????全局變量??
@??????????實變量?
[a-z]??????局部變量?
[A-Z]??????常量?
唯一的例外是Ruby的偽變量:self,它永遠指向當前正執行著的對象或未初始化變量的空值(meaningless?value)nil.雖然這兩者
的命名都像是局部變量,但?self?卻是個由解釋器把持的全局變量,而?nil?實際上是個常量.既然只有這兩種意外,他們并不會過多的干擾我們.
你并能向?self?或?nil?賦值.下面的例子中,?main作為?self?的值,指向最高層的對象:
ruby>?self
???main
ruby>?nil
???nil
全局變量:全局變量由$開頭.它們可以在程序的任何位置訪問到.在初始化前,全局變量有一個特殊的值?nil.
ruby>?$foo
???nil
ruby>?$foo?=?5
???5
ruby>?$foo
???5
應謹慎使用全局變量.由于在任何地方都可以被寫因此他們相當危險.濫用全局變量會導致很難隔離臭蟲;同時也視為程序的設計未經嚴格考慮.當你發現必須要使用全局變量時,記得給它一個不會在其它地方一不小心就用到的描述性名字(像上面那樣叫$foo可能不是一個好想法).
全局變量的好處是其可以被跟蹤;你可以做一個當變量值改變時被調用的過程.
ruby>?trace_var?:$x,?proc{print?"$x?is?now?",?$x,?"\n"}
???nil
ruby>?$x?=?5
$x?is?now?5
???5
當一個全局變量(改變時)作為一個過程的激發器,我們也管它叫活動變量(active?variable).比如說,它可用于保持GUI顯示的更新.
這里列出了一些以$打頭并跟單個字符的特殊變量.比如,$$包含了Ruby解釋器的進程id,它是只讀的.這里是主要的系統變量以及它們的含義(細節可在Ruby的參考手冊中查到):
$!??最近一次的錯誤信息?
$@??錯誤產生的位置?
$_??gets最近讀的字符串??
$.??解釋器最近讀的行數(line?number)?
$&??最近一次與正則表達式匹配的字符串?
$~??作為子表達式組的最近一次匹配??
$n??最近匹配的第n個子表達式(和$~[n]一樣)??
$=??是否區別大小寫的標志??
$/??輸入記錄分隔符?
$\??輸出記錄分隔符?
$0??Ruby腳本的文件名?
$*??命令行參數?
$$??解釋器進程ID?
$???最近一次執行的子進程退出狀態?
上面的?$_?和?$~?都有作用范圍.它們的名字暗示其為全局的,但它們一般都是這樣用的,關于它們的命名有歷史上的原因。
實例變量:
一個實例變量由@開頭,它的范圍限制在?self?對象內.兩個不同的對象,即使屬于同一個類,也可以擁有不同值的實變量.從對象外部來看,實變量不能改變
甚至觀察(比如,?Ruby的實變量從來不是公用的),除非方法由程序員明確聲明.像全局變量一樣,實變量在初始前的值是nil.
Ruby的實例變量用不著聲明.這暗含著對象的彈性結構.實際上,每個實變量都是在第一次出現時動態加入對象的.
ruby>?class?InstTest
????|???def?set_foo(n)
????|?????@foo?=?n
????|???end
????|???def?set_bar(n)
????|?????@bar?=?n
????|???end
????|?end
???nil
ruby>?i?=?InstTest.new
???#<InstTest:0x83678>
ruby>?i.set_foo(2)
???2
ruby>?i
???#<InstTest:0x83678?@foo=2>
ruby>?i.set_bar(4)
???4
ruby>?i
???#<InstTest:0x83678?@foo=2,?@bar=4>
注意上例中直到調用了?set_bar方法?i?才報告?@bar?的值.
局部變量:局部變量由小寫字母或下劃線(_)開頭.局部變量不像全局和實變量一樣在初始化前含nil值.
ruby>?$foo
???nil
ruby>?@foo
???nil
ruby>?foo
ERR:?(eval):1:?undefined?local?variable?or?method?`foo'?for?main(Object)
對局部變量的第一次賦值做的很像一次聲明.如果你指向一個未初始化的局部變量,Ruby解釋器會認為那是一個方法的名字;正如上面所見錯誤
信息的.
一般的,局部變量的范圍會是
- proc{...}?
- loop{...}?
- def...end?
- class...end?
- module...end?
- 整個程序(除非符合上面某個條件)
下面的例子,define?是一個檢查標識符是否已定義的操作符.如果已定義它將返回標識符的描述,否則返回nil.正如你所見的,bar的范圍是
loop的局部變量;當loop退出時,bar無定義.
ruby>?foo?=?44;?print?foo,?"\n";?defined??foo
44
???"local-variable"
ruby>?loop{bar=45;?print?bar,?"\n";?break};?defined??bar
45
???nil
一個范圍內的過程對象共享這個范圍內的局部變量.這里,局部變量?bar?由?main?和過程對象?p1,?p2共享:
ruby>?bar=0
???0
ruby>?p1?=?proc{|n|?bar=n}
???#<Proc:0x8deb0>
ruby>?p2?=?proc{bar}
???#<Proc:0x8dce8>
ruby>?p1.call(5)
???5
ruby>?bar
???5
ruby>?p2.call
???5
注意開始的"bar=0"不能省略;此賦值允許bar的范圍被?p1和?p2共享.不然?p1,?p2?將會分別生成并處理它們自己的局部變量?bar,?調用?p2?
也將導致"未定義局部變量或方法"錯誤.
過程對象的強大在于它們能被作為參數傳遞:共享的局部變量即使傳遞出原范圍也仍然有效.
ruby>?def?box
????|???contents?=?15
????|???get?=?proc{contents}
????|???set?=?proc{|n|?contents?=?n}
????|???return?get,?set
????|?end
???nil
ruby>?reader,?writer?=?box
???[#<Proc:0x40170fc0>,?#<Proc:0x40170fac>]?
ruby>?reader.call
???15
ruby>?writer.call(2)
???2
ruby>?reader.call
???2
Ruby對待范圍的辦法相當聰明.顯然,上面例子里?contents?變量是由?reader?和?writer?共享的.我們也可以像上面那樣創造多對使用box的
reader-writer;每一對共享一個?contents?變量,對之間不相干擾.
ruby>?reader_1,?writer_1?=?box
???[#<Proc:0x40172820>,?#<Proc:0x4017280c>]
ruby>?reader_2,?writer_2?=?box
???[#<Proc:0x40172668>,?#<Proc:0x40172654>]
ruby>?writer_1.call(99)
???99
ruby>?reader_1.call
???99
ruby>?reader_2.call
???15
類常量:一個常量由大寫字母開頭.它應最多被賦值一次.在Ruby的當前版本中,常量的再賦值只會產生警告而不是錯誤(non-ANSI版的eval.rb不會報告這一警告)
ruby>fluid=30
???30
ruby>fluid=31
???31
ruby>Solid=32
???32
ruby>Solid=33
???(eval):1:?warning:?already?initialized?constant?Solid
???33
常量可以定義在類里,但不像實變量,它們可以在類的外部訪問.
ruby>?class?ConstClass
????|???C1=101
????|???C2=102
????|???C3=103
????|???def?show
????|?????print?C1,"?",C2,"?",C3,"\n"
????|???end
????|?end
???nil
ruby>?C1
ERR:?(eval):1:?uninitialized?constant?C1
ruby>?ConstClass::C1
???101
ruby>?ConstClass.new.show
101?102?103
???nil
常量也可以定義在模塊里.
ruby>?module?ConstModule
????|???C1=101
????|???C2=102
????|???C3=103
????|???def?showConstants
????|?????print?C1,"?",C2,"?",C3,"\n"
????|???end
????|?end
???nil
ruby>?C1
ERR:?(eval):1:?uninitialized?constant?C1
ruby>?include?ConstModule
???Object
ruby>?C1
???101
ruby>?showConstants
101?102?103
???nil
ruby>?C1=99??#?not?really?a?good?idea
???99
ruby>?C1
???99
ruby>?ConstModule::C1??#?the?module's?constant?is?undisturbed?...
???101
ruby>?ConstModule::C1=99?
ERR:?(eval):1:?compile?error
(eval):1:?parse?error
ConstModule::C1=99
????????????????^
ruby>?ConstModule::C1??#?..?regardless?of?how?we?tamper?with?it.
???101