本文的目的是為了找出為什么.NET程序員都想學(xué)習(xí)并使用Ruby,并探索Ruby語(yǔ)言的核心語(yǔ)法。
微軟的IronRuby項(xiàng)目為Windows平臺(tái)帶來(lái)了強(qiáng)大的動(dòng)態(tài)語(yǔ)言,Ruby編程語(yǔ)言是一個(gè)現(xiàn)代的,面向?qū)ο蟮幕菊Z(yǔ)言,它的語(yǔ)法靈感來(lái)自Perl和Smalltalk語(yǔ)言,它是由一名日本人松本行弘(外號(hào)Matz)發(fā)明的,用他的話說(shuō),他是想發(fā)明一種語(yǔ)言比Perl更強(qiáng)大,同時(shí)比Python更面向?qū)ο蟮木幊陶Z(yǔ)言,在“http://www.linuxdevcenter.com/pub/a/linux/2001/11/29/ruby.html”有一篇對(duì)松本行弘專訪文章,大家可以去看看。于是Ruby被設(shè)計(jì)為非常貼近自然語(yǔ)言,作者的原意就是要減少編程時(shí)候的不必要的瑣碎時(shí)間,令編寫程序的人高興,他于1996年發(fā)布了1.0版本。
這么多年來(lái),Ruby一直鮮為人知,但它的功能已經(jīng)遠(yuǎn)遠(yuǎn)超出了最初設(shè)計(jì)時(shí)的想法:以最簡(jiǎn)化的方法操作數(shù)據(jù)和環(huán)境。我第一次“玩”它還是在幾年前,那時(shí)我正在尋找一種替換處理自動(dòng)管理任務(wù)的批處理文件的方法。
Ruby真正開始流行還得從一個(gè)來(lái)自伊利諾斯洲芝加哥市的名叫37signals小公司說(shuō)起,它們發(fā)布了一個(gè)名叫Rails的Web應(yīng)用程序框架,這個(gè)新的框架吸取了已經(jīng)被證明是可靠的Model-View-Controller和ActiveRecord模型的經(jīng)驗(yàn),并且添加了一些新的思想,如convention over configuration,導(dǎo)致它實(shí)現(xiàn)了太多的目標(biāo),幾乎不需要編碼了。
RubyCLR和IronRuby
在2006年早些時(shí)候,John Lam發(fā)布了一個(gè)開源項(xiàng)目,叫做RubyCLR,它在Ruby和.NET之間起到一個(gè)橋梁的作用,它允許用戶可以直接從Ruby訪問.NET平臺(tái)豐富的資源,甚至將Ruby對(duì)象都暴露給CLR了,這個(gè)項(xiàng)目非常有雄心,但它沒有打算將Ruby向.NET靠攏,而是打算讓這兩個(gè)世界相互對(duì)話,你仍然需要在你的機(jī)器上按照Ruby運(yùn)行時(shí)環(huán)境。
RubyCLR項(xiàng)目為人們理解如何將Ruby和.NET和諧地溶合到一起邁出了關(guān)鍵的第一步,John的工作沒有引起人們的注意,2006年末,他在他的博客上宣布加入微軟新成立的動(dòng)態(tài)語(yǔ)言運(yùn)行時(shí)環(huán)境(DLR)團(tuán)隊(duì),在John宣布前幾個(gè)月,微軟發(fā)布了IronPython的1.0版本,它是Python語(yǔ)言在.NET框架上一個(gè)新的實(shí)現(xiàn),動(dòng)態(tài)語(yǔ)言運(yùn)行時(shí)環(huán)境在IronPython上工作,它在.NET框架構(gòu)建了一個(gè)運(yùn)行環(huán)境,允許動(dòng)態(tài)語(yǔ)言進(jìn)入.NET。
John和他的團(tuán)隊(duì)在2007年的MIX大會(huì)上宣布了IronRuby,可能真正讓人吃驚的是IronRuby項(xiàng)目本身是微軟的第一個(gè)真正意義上的開源.NET語(yǔ)言,不僅可以得到源代碼,而且還可以獲取來(lái)自社區(qū)的貢獻(xiàn)。
IronRuby仍然處于發(fā)展階段,然而偶然也會(huì)刪掉已經(jīng)可以利用的東西,這些東西通常是其它項(xiàng)目的一部分,如最近發(fā)布的Silverlight 2.0 Beta 2,這些后續(xù)的項(xiàng)目也放在源代碼樹中了,并且也有相應(yīng)的郵件列表。
為什么要學(xué)習(xí)Ruby?
我最喜歡的一本書叫做《程序員實(shí)務(wù):從熟練工到大師》【英文名是《The Pragmatic Programmer: From Journeyman to Master》】,該書的作者鼓勵(lì)程序員每年學(xué)習(xí)一門新的編程語(yǔ)言,對(duì)于我而言,當(dāng)我學(xué)習(xí)了Ruby語(yǔ)言后,大大地改變了我的專業(yè)范圍。
Ruby是一門完全面向?qū)ο蟮恼Z(yǔ)言,這意味著在系統(tǒng)中每一樣打交道的東西都是對(duì)象,包括直接的值,如數(shù)字,即使是類,也是由新創(chuàng)建的對(duì)象實(shí)例組成的模板。
因?yàn)镽uby是一個(gè)動(dòng)態(tài)語(yǔ)言,你會(huì)發(fā)現(xiàn)類型已經(jīng)變得不太重要了,當(dāng)一個(gè)類函數(shù)以參數(shù)形式獲取到一個(gè)對(duì)象時(shí),不需要指定對(duì)象需要的類型。實(shí)際上,Ruby沒有編譯器,因此,可能直到傳遞給類函數(shù)的對(duì)象不滿足方法的需要時(shí),你才會(huì)發(fā)現(xiàn)這一點(diǎn)。
如果你象我?guī)啄昵澳菢?,你也許會(huì)發(fā)現(xiàn)這個(gè)概念讓你不安,如果沒有編譯器,那么你可能要盡可能最快地在運(yùn)行前就了解代碼中的錯(cuò)誤,而不用等到運(yùn)行時(shí)才知道。如果你還是習(xí)慣于讓編譯器告訴你錯(cuò)誤,那你就不用選擇Ruby了。
正是由于以前編譯器能夠報(bào)告錯(cuò)誤,如類型不匹配,當(dāng)你編寫一個(gè)類函數(shù)時(shí),你可能希望“這里的對(duì)象必須能夠做到foo和bar”,然后創(chuàng)建一個(gè)接口叫做IFooBar,看起來(lái)這是一個(gè)不錯(cuò)的解決方案,但當(dāng)你想使用其它的在IfooBar之前創(chuàng)建的類時(shí)(特別是那些來(lái)自框架的類型),你就會(huì)失敗了。
作者提醒:IronRuby還沒有成為主流的工具,你可以使用Ruby的標(biāo)準(zhǔn)版本進(jìn)行學(xué)習(xí),如果你想實(shí)驗(yàn)后面的例子,可以從http://rubyinstaller.rubyforge.org/下載。
Ruby示例
學(xué)習(xí)Ruby或一門新的編程語(yǔ)言最好的方法就是多練習(xí),研究它的交互接口,大多數(shù)動(dòng)態(tài)語(yǔ)言都有交互提示符,稱之為讀-執(zhí)行-打印環(huán)(即REPL,Read-Execute-Print Loop),Ruby中的REPL程序叫做irb(即交互式Ruby,interactive Ruby)。
當(dāng)你執(zhí)行irb程序時(shí),你會(huì)看到一個(gè)irb提示符,如:
C:\Users\Brad> irb irb(main):001:0>
當(dāng)你在irb提示符后敲入命令時(shí),Ruby解釋程序就會(huì)評(píng)估它們,并將結(jié)果輸出到你屏幕上,與irb類似的REPL是學(xué)習(xí)一門語(yǔ)言的優(yōu)秀方法:每次一條語(yǔ)句。
下面對(duì)irb做一個(gè)簡(jiǎn)單的介紹,在irb提示符后,敲入5+2,并回車,告訴Ruby計(jì)算這個(gè)表達(dá)式的值:
irb(main):001:0> 5 + 2 => 7
irb(main):001:0>部分是irb的提示符,當(dāng)你敲入5+2并回車時(shí),irb就將結(jié)果輸出到屏幕上,如這里的=> 7,=> 是irb顯示輸出結(jié)果時(shí)使用的提示符。
如果Ruby認(rèn)為你還沒有完成表達(dá)式的書寫,它允許你繼續(xù)換行書寫,如當(dāng)你敲入5+2+時(shí)就按了回車,Ruby認(rèn)為你還有一部分沒有輸入完畢,它會(huì)繼續(xù)讓你在下一行輸入,如:
irb(main):002:0> 5 + 2 + irb(main):003:0* 13 => 20
第二行的提示符變?yōu)樾翘?hào)(*)了,而不是“>”,這樣你就知道你在完成前面沒有完成的表達(dá)式。
基礎(chǔ)類型
如果一門編程語(yǔ)言不能處理數(shù)字,那就不值得學(xué)習(xí)和使用,Ruby當(dāng)然能夠滿足算術(shù)運(yùn)算了,如:
irb(main):004:0> 3 + 4 => 7 irb(main):005:0> 3 * 4 => 12 irb(main):006:0> 3 - 4 => -1 irb(main):007:0> 3 / 4 => 0 irb(main):008:0> 3.0 / 4.0 => 0.75 irb(main):009:0> 0xF => 15 irb(main):010:0> 0x3 * 0xA => 30
正如你所看到的,Ruby支持整數(shù)和浮點(diǎn)類型,甚至可以接收常用的十六進(jìn)制整數(shù),但0x3 * 0xA的結(jié)果是以十進(jìn)制的形式顯示的,即顯示結(jié)果是30而不是0x1E。
因?yàn)樵?NET中,數(shù)字也是真實(shí)的對(duì)象,因此,你可以在它們上面調(diào)用類函數(shù),如:
irb(main):011:0> 14.to_s => "14"
在c++中不要這樣做。
to_s類函數(shù)的功能是將一個(gè)對(duì)象轉(zhuǎn)換成一個(gè)字符串,因此,14.to_s返回的結(jié)果是"14",和.NET中的to_string()函數(shù)一樣,to_s函數(shù)實(shí)際上是一個(gè)對(duì)象函數(shù),因此,在Ruby中你可以將任何東西轉(zhuǎn)換成字符串。
字符串
Ruby的字符串具備完整的操作支持,如:
irb(main):012:0> "hello" + "there" => "hellothere" irb(main):013:0> "Reader".length => 6 irb(main):014:0> "Reader".reverse => "redaeR" irb(main):015:0> "reader".capitalize => "Reader" irb(main):016:0> "Reader".include?("foo") => false irb(main):017:0> "Reader".include?("ade") => true irb(main):018:0> " Reader ".strip => "Reader" irb(main):019:0> "Reader".gsub("e", "f") => "Rfadfr" irb(main):020:0> "Reader".delete("ea") => "Rdr" irb(main):021:0> "a" < "b" => true
幾乎可以使用所有的字符串操作符,可能有的你還從來(lái)都沒有使用過,如下面的代碼按字母順序測(cè)試某個(gè)字符串是否位于其他兩個(gè)之間:
irb(main):022:0> "Bob".between? "Adam", "Chris" => true
乘法操作符可以讓給定的字符串重復(fù)顯示指定的數(shù)量,如:
irb(main):023:0> "hi" * 5 => "hihihihihi"
Crypt函數(shù)為字符串提供了一個(gè)單向哈希加密功能,在存儲(chǔ)敏感數(shù)據(jù)如密碼時(shí)就可以使用它,如:
irb(main):024:0> "Reader".crypt("ab") => "abofgDjq6JNJo"
字符
Ruby沒有內(nèi)置的字符類型,它象數(shù)字一樣表現(xiàn)字符,可以是?語(yǔ)法來(lái)表示一個(gè)字符常量,你可以使用chr函數(shù)將一個(gè)數(shù)字轉(zhuǎn)換成一個(gè)等價(jià)的字符串,如:
irb(main):025:0> "Reader"[2] => 97 irb(main):026:0> ?a => 97 irb(main):027:0> 97.chr => "a"
賦值
其實(shí)執(zhí)行這個(gè)操作并沒什么用途,除非你可以將其存儲(chǔ)起來(lái)方便后面使用,如:
irb(main):028:0>x = 42 =>42
字符串有一個(gè)特殊的語(yǔ)法,允許嵌入式賦值,這個(gè)賦值不僅僅局限于簡(jiǎn)單的變量替換,它是一個(gè)完整的賦值,如:
irb(main):029:0> "The answer is #{x}!" => "The answer is 42!" irb(main):030:0> "The answer is #{6 * 7}!" => "The answer is 42!"
可以使用單引號(hào)將字符串引起來(lái)避免這種賦值,注意是單引號(hào),不是雙引號(hào),如:
irb(main):031:0> 'The answer is #{x}!' => "The answer is \#{x}!"
數(shù)組
Ruby中的數(shù)組與.NET 1.0中的ArrayList類很接近,它們的大小都是可變的,用于存儲(chǔ)任意類型的數(shù)據(jù),從0開始編號(hào),如:
irb(main):032:0> a = ["hello", 42, "world"] => ["hello", 42, "world"] irb(main):033:0> a << 5.0 * 7.5 => ["hello", 42, "world", 37.5] irb(main):034:0> a[0] => "hello" irb(main):035:0> a[6] = 'hi' * 2 => "hihi" irb(main):036:0> a => ["hello", 42, "world", 37.5, nil, nil, "hihi"] irb(main):037:0> a[99] => nil
前面的代碼顯示了如何使用<<操作符向數(shù)組末尾追加項(xiàng)目,以及獲取或設(shè)置值使用的指針操作符[],當(dāng)你向數(shù)組末尾添加一個(gè)項(xiàng)目時(shí),Ruby使用零值填充數(shù)組中的“洞”,當(dāng)你訪問數(shù)組外的值時(shí),Ruby返回零值而不是異常。
你可以使用一個(gè)范圍的指針將數(shù)組分片,也可以使用負(fù)的指針從后向前訪問數(shù)組,-1就是最后一項(xiàng),-2是倒數(shù)第二項(xiàng),以此類推,但不能使用反向范圍獲取反向分片,你可以使用一個(gè)正向范圍,然后調(diào)用reverse方法,如:
irb(main):038:0> a[-1] => "hihi" irb(main):039:0> a[1..3] =>[42, "world", 37.5] irb(main):040:0>a[2..-2] =>["world", 37.5, nil, nil] irb(main):041:0>a[-4..-1] =>[37.5, nil, nil, "hihi"] irb(main):042:0>a[-1..-4] # 不能工作 =>[] irb(main):043:0>a[-4..-1].reverse # 能夠工作 =>["hihi", nil, nil, 37.5]
和字符串一樣,你會(huì)發(fā)現(xiàn)有多個(gè)唯一對(duì)數(shù)組有用的類函數(shù),如:
irb(main):044:0> a => ["hello", 42, "world", 37.5, nil, nil, "hihi"] irb(main):045:0> a.compact => ["hello", 42, "world", 37.5, "hihi"] irb(main):046:0> a.join => "hello42world37.5hihi" irb(main):047:0> [10, 75, 6, 29].sort => [6, 10, 29, 75] irb(main):048:0> [[1, 2, 3], [4, 5, 6]] => [[1, 2, 3], [4, 5, 6]] irb(main):049:0> [[1, 2, 3], [4, 5, 6]].flatten => [1, 2, 3, 4, 5, 6]
散列
Ruby的最后一個(gè)核心數(shù)據(jù)結(jié)構(gòu)是散列,與.NET 1.0中的散列表類似,它是一個(gè)聯(lián)合數(shù)組,它的鍵值可以是任意類型的值,它們指向的數(shù)據(jù)也可以是任意類型的數(shù)據(jù),實(shí)際上,大部分散列使用的是符號(hào)作為鍵值。
使用{}語(yǔ)法聲明散列,并且使用key => value格式聲明初始值,在散列中獲取或設(shè)置值時(shí)都可以使用鍵值操作符,如:
irb(main):050:0> h = {:foo=>'bar', :baz=>'biff'} => {:foo=>"bar", :baz=>"biff"} irb(main):051:0> h[:foo] => "bar" irb(main):052:0> h[:unknown] => nil irb(main):053:0> h[:baz] = "new" => "new" => {:foo=>"bar", :baz=>"new"} irb(main):054:0> h.entries => [[:foo, "bar"], [:baz, "new"]]
變量
Ruby中的變量和類函數(shù)名都是以小寫字母開頭的,可以包括字母、數(shù)字和下劃線。本地變量沒有前綴,實(shí)例變量以@開頭,全局變量以$開頭。
在使用變量前無(wú)需聲明,未初始化的變量有一個(gè)零值,下面是幾個(gè)預(yù)定義的變量:
nil表示一個(gè)“無(wú)”對(duì)象,與.NET中的null類似,除了nil是一個(gè)實(shí)例化的NilClass類外。
true和false分別是實(shí)例化的TrueClass和FalseClass。
在類函數(shù)中使用時(shí),self指向調(diào)用類函數(shù)的對(duì)象實(shí)例;在一個(gè)類中使用時(shí),它指的是實(shí)例化的類對(duì)象本身。
__FILE__ 和__LINE__返回當(dāng)前執(zhí)行文件和那個(gè)文件中的行號(hào)。
符號(hào)
Ruby有一個(gè)特殊類型的字符串,叫做符號(hào),因?yàn)樽址赗uby中是可以被修改的,使用它們作為散列鍵是很慢的,而且有一些情況是不能預(yù)測(cè)的。
除了它們是以冒號(hào)(:)開頭外,符號(hào)的命名規(guī)則和變量的命名規(guī)則一致,你不能改變符號(hào)的值,兩個(gè)名字相同的符號(hào)它們的身份就一樣,它們可以作為優(yōu)秀的散列鍵,查找請(qǐng)求只需要比較整數(shù)值,而不是與一個(gè)可變長(zhǎng)字符串的值進(jìn)行對(duì)比。
類
Ruby中的所有事物都是對(duì)象,所有對(duì)象都是類的實(shí)例,為了探索類是個(gè)什么東西,在它上面調(diào)用類函數(shù):
5.class => Fixnum (2 ** 96).class => Bignum 7.5.class => Float (1..10).class => Range "foo".class => String /^foo[a-e]$/.class => Regexp :foo.class => Symbol [].class => Array {}.class => Hash
塊和閉包
雖然這與.NET 1.X中的事件處理程序類似,但當(dāng)你想處理它們時(shí)還是必須要定義完整的類函數(shù)來(lái)連接這些事件,這就導(dǎo)致需要?jiǎng)?chuàng)建大量的類函數(shù),因?yàn)榭蚣苄枰?div style="height:15px;">
irb(main):001:0> h = {:foo=>'bar', :hi=>'there'} => {:foo=>"bar", :hi=>"there"} irb(main):002:0> h.each_key {|k| puts k} foo hi => {:foo=>"bar", :hi=>"there"} irb(main):003:0> h.each {|k,v| puts "#{k}: #{v}"} foo: bar hi: there => {:foo=>"bar", :hi=>"there"}
正如你所看到的,Ruby中塊的語(yǔ)法是相當(dāng)簡(jiǎn)潔的:通常使用一對(duì)大括號(hào)打開塊和關(guān)閉塊,使用|x,y|語(yǔ)法標(biāo)出傳遞給塊的變量。
Ruby中的塊和閉包類似,正如.NET 2.0中的匿名委派,這意味著它們有權(quán)訪問它們封裝作用域的值,即使那個(gè)作用域退出后也可以訪問。下面是一個(gè)將幾個(gè)值相乘的閉包示例:
irb(main):004:0> n = [5, 6, 10] => [5, 6, 10] irb(main):005:0> t = 1 => 1 irb(main):006:0> n.each { |i| t *= i } => [5, 6, 10] irb(main):007:0> t => 300
irb(main):008:0> t = 1 => 1 irb(main):009:0> f = lambda { |i| t *= i } => # < PRE>