作為一名程序員,需要在Ruby環境中工作,本文為大家詳解演示Ruby的容易被誤解的幾個特性,如果它們來自不同的環境要怎么處理呢?今天這篇文章是武林技術頻道小編整理的相關知識,一起進入下文看看吧!
簡介: 假設您是一名 C++ 開發人員,您需要使用 Ruby 快速執行一些原型設計。當您拿起一本 Ruby 參考書籍(比如 Pickaxe)或瀏覽 Ruby 網站時,會看到一些熟悉的構造,比如類聲明、線程支持和異常處理。正當您認為自己了解 Ruby 的工作原理之時,您意識到了,您 Ruby 代碼中的并發機制與 Boost 線程工作原理不一樣,catch 和 throw 也與它們看上去的大不相同,而且其他人在其 Ruby 腳本中各處使用了名為 self 的關鍵詞。歡迎來到 Ruby 的世界中!
● Ruby 類層次結構
● Ruby 中的單例方法
● self 關鍵詞
● method_missing 方法
● 異常處理
● 線程
?
注意:本文中所有的代碼均進行測試,且基于 Ruby 版本 1.8.7。
?
Ruby 中的類層次結構
Ruby 中的類層次結構會很棘手。創建一個 Cat 類型的類并開始探討其層次結構(參見 清單 1)。
清單 1. Ruby 中的隱式類層次結構
?
ruby;">irb(main):092:0> class Catirb(main):093:1> end=> nil irb(main):087:0> c = Cat.new=> #<Cat:0x2bacb68>irb(main):088:0> c.class=> Catirb(main):089:0> c.class.superclass=> Objectirb(main):090:0> c.class.superclass.superclass=> nilirb(main):091:0> c.class.superclass.superclass.superclassNoMethodError: undefined method `superclass' for nil:NilClass from (irb):91 from :0
Ruby 中的所有對象(甚至用戶定義的對象)都是 Object 類的后代,這在清單 1 中清晰可見。這與 C++ 是鮮明的對比。這一點也不像普通數據類型,例如 C/C++ int 或 double。清單 2 顯示了整數 1 的類層次結構。
清單 2. 整數 1 的類層次結構
?
irb(main):100:0> 1.class=> Fixnumirb(main):101:0> 1.class.superclass=> Integerirb(main):102:0> 1.class.superclass.superclass=> Numericirb(main):103:0> 1.class.superclass.superclass.superclass=> Object
到目前為止一切順利。現在您知道了類本身是 Class 類型的對象。而 Class 最終派生自 Object,如 清單 3 中所示使用 Ruby 內置的 String 類。
清單 3. 類的類層次結構
?
irb(main):100:0> String.class=> Classirb(main):101:0> String.class.superclass=> Moduleirb(main):102:0> String.class.superclass.superclass=> Object
Module 是 Class 的基類,但是使用它時有一點要注意,即您不能直接實例化用戶定義的 Module 對象。如果您不想深入 Ruby 內部,最好考慮與 C++ 命名空間有類似特征的 Module:您可以定義您自己的方法、常量、等等。您在 Class 中包含了一個 Module,以及 voilà,Module 的所有元素現在會魔法般地成為 Class 的元素。清單 4 提供了一個示例。
清單 4. Module 不能進行直接實例化,并且只能與類一同使用
irb(main):020:0> module MyModuleirb(main):021:1> def helloirb(main):022:2> puts "Hello World"irb(main):023:2> endirb(main):024:1> endirb(main):025:0> test = MyModule.newNoMethodError: undefined method `new' for MyModule:Module from (irb):25irb(main):026:0> class MyClassirb(main):027:1> include MyModuleirb(main):028:1> end=> MyClassirb(main):029:0> test = MyClass.new=> #<MyClass:0x2c18bc8>irb(main):030:0> test.helloHello World=> nil
下面再重申一下重點:當您使用 Ruby 編寫 c = Cat.new 時,c 是派生自 Object 的 Cat 類型的一個對象。Cat 類是 Class 類型的一個對象,Class 派生自 Module,而 Module 又派生自 Object。因此該對象及其類型都是有效的 Ruby 對象。
單例方法和可編輯類
現在,看一下單例方法。假設您想使用 C++ 建模類似于人類社會的東西。那么您會如何做呢?定義一個名為 Human 的類,然后定義數百萬的 Human 對象?這更像是在建模一個呆板的社會;每個人必須具惟一的特征。Ruby 的單例方法在這里就派上了用場,如 清單 5 所示。
清單 5. Ruby 中的單例方法
?
irb(main):113:0> y = Human.new=> #<Human:0x319b6f0>irb(main):114:0> def y.paintirb(main):115:1> puts "Can paint"irb(main):116:1> end=> nilirb(main):117:0> y.paintCan paint=> nilirb(main):118:0> z = Human.new=> #<Human:0x3153fc0>irb(main):119:0> z.paintNoMethodError: undefined method `paint' for #<Human:0x3153fc0> from (irb):119
Ruby 中的單例方法 是僅與特定對象關聯的方法,不能用于一般的類。它們的前綴是對象名稱。在 清單 5 中,paint 方法特定于 y對象,而且僅限于 y 對象;z.paint 導致一個 “方法未定義” 錯誤。您可以調用 singleton_methods 來查明一個對象中的單例方法列表:
?
irb(main):120:0> y.singleton_methods=> ["paint"]
不過在 Ruby 中有另一種定義單例方法的方式。看看 清單 6 中的代碼。
清單 6. 創建單例方法的另一種方式
?
irb(main):113:0> y = Human.new=> #<Human:0x319b6f0>irb(main):114:0> class << yirb(main):115:1> def singirb(main):116:1> puts "Can sing"irb(main):117:1> endirb(main):118:1>end=> nilirb(main):117:0> y.singCan sing=> nil
?
?
?
清單 5 還開創了新的可能性,可以添加新方法到用戶定義的類和內置的 Ruby 現有類,比如 String。這在 C++ 中是不可能實現的,除非您能夠訪問您使用的類的源代碼。再次觀察 String 類(清單 7)。
清單 7. Ruby 允許您修改一個現有的類
irb(main):035:0> y = String.new("racecar")=> "racecar"irb(main):036:0> y.methods.grep(/palindrome/)=> [ ]irb(main):037:0> class Stringirb(main):038:1> def palindrome?irb(main):039:2> self == self.reverseirb(main):040:2> endirb(main):041:1> endirb(main):050:0> y.palindrome?=> true
清單 7 清楚地展示了如何編輯一個現有的 Ruby 類來添加您自行選擇的方法。這里,我添加了 palindrome? 方法到 String 類。因此 Ruby 類在運行時是可編輯的(一個強大的屬性)。
現在您對 Ruby 的類層次結構和單例有了一定的認識,接下來我們來看 self。注意,在定義 palindrome? 方法時我使用了 self。
發現 self
self 關鍵詞的最常見用法可能就是在 Ruby 類中聲明一個靜態方法,如 清單 8 所示。
?
?
清單 8. 使用 self 聲明類的靜態方法
class SelfTest def self.test puts "Hello World with self!" endend class SelfTest2 def test puts "This is not a class static method" endend SelfTest.testSelfTest2.test
從 清單 8 的輸出中可以看到(如 清單 9 所示),沒有對象您無法調用非靜態方法。該行為類似于 C++。
清單 9. 在沒有對象的情況下調用非靜態方法時會出錯
?
irb(main):087:0> SelfTest.testHello World with self!=> nilirb(main):088:0> SelfTest2.testNoMethodError: undefined method 'test' for SelfTest2:Class from (irb):88
在探討 self 更深奧的用途和含義之前,注意您也可以通過在方法名稱前面加上類名來在 Ruby 中定義一個靜態方法:
class TestMe def TestMe.test puts "Yet another static member function" endend TestMe.test # works fine
清單 10 提供了 self 的一個更有趣但不太容易找到的用法。
清單 10. 使用元類來聲明靜態方法
?
class MyTest class << self def test puts "This is a class static method" end endend MyTest.test # works fine
該段代碼以一種稍微不同的方式將 test 定義為一個類靜態方法。要了解究竟發生了什么,您需要看一下 class << self 語法的一些細節。class << self … end 創建一個元類。在方法查找鏈中,在訪問對象的基類之前先搜索該對象的元類。如果您在元類中定義一個方法,可以在類上調用該方法。這類似于 C++ 中靜態方法的概念。
可以訪問一個元類嗎?是的:只需從 class << self … end 內返回 self。注意,在一個 Ruby 類聲明中,您沒有義務僅給出方法定義。清單 11 顯示了元類。
清單 11. 理解元類
irb(main):198:0> class MyTestirb(main):199:1> end=> nilirb(main):200:0> y = MyTest.new=> #< MyTest:0x2d43fe0>irb(main):201:0> z = class MyTestirb(main):202:1> class << selfirb(main):203:2> selfirb(main):204:2> endirb(main):205:1> end=> #<Class: MyTest >irb(main):206:0> z.class=> Classirb(main):207:0> y.class=> MyTest
回到 清單 7 的代碼,您會看到 palindrome 被定義為 self == self.reverse。在該上下文中,self 與 C++ 沒有什么區別。C++和 Ruby 中的方法都需要一個操作對象,以修改或提取狀態信息。self 是指這里的這個對象。注意,可以通過附加 self 前綴來選擇性地調用公共方法,指明方法付諸作用的對象,如 清單 12 所示。
清單 12. 使用 self 調用方法
?
irb(main):094:0> class SelfTest3irb(main):095:1> def fooirb(main):096:2> self.bar()irb(main):097:2> endirb(main):098:1> def barirb(main):099:2> puts "Testing Self"irb(main):100:2> endirb(main):101:1> end=> nilirb(main):102:0> test = SelfTest3.new=> #<SelfTest3:0x2d15750>irb(main):103:0> test.fooTesting Self=> nil
在 Ruby 中您無法通過附加 self 關鍵詞前綴來調用私有方法。對于一名 C++ 開發人員,這可能會有點混淆。清單 13 中的代碼明確表示,self 不能用于私有方法:對私有方法的調用只能針對隱式對象。
清單 13. self 不能用于私有方法調用
?
irb(main):110:0> class SelfTest4irb(main):111:1> def method1irb(main):112:2> self.method2irb(main):113:2> endirb(main):114:1> def method3irb(main):115:2> method2irb(main):116:2> endirb(main):117:1> privateirb(main):118:1> def method2irb(main):119:2> puts "Inside private method"irb(main):120:2> endirb(main):121:1> end=> nilirb(main):122:0> y = SelfTest4.new=> #<SelfTest4:0x2c13d80>irb(main):123:0> y.method1NoMethodError: private method `method2' called for #<SelfTest4:0x2c13d80> from (irb):112:in `method1'irb(main):124:0> y.method3Inside private method=> nil
由于 Ruby 中的一切都是對象,當在 irb 提示符上調用 self 時您會得到以下結果:
irb(main):104:0> self=> mainirb(main):105:0> self.class=> Object
一啟動 irb,Ruby 解釋器就為您創建主對象。這一主對象在 Ruby 相關的文章中也被稱為頂層上下文。
關于 self 就介紹這么多了。下面我們接著來看動態方法和 method_missing 方法。
method_missing 揭秘
看一下 清單 14 中的 Ruby 代碼。
清單 14. 運行中的 method_missing
?
irb(main):135:0> class Testirb(main):136:1> def method_missing(method, *args)irb(main):137:2> puts "Method: #{method} Args: (#{args.join(', ')})"irb(main):138:2> endirb(main):139:1> end=> nilirb(main):140:0> t = Test.new=> #<Test:0x2c7b850>irb(main):141:0> t.f(23)Method: f Args: (23)=> nil
在 清單 15 中查看更多 voodoo。
清單 15. 使用 send 方法將參數傳遞給一個例程
?
irb(main):142:0> class Testirb(main):143:1> def method1(s, y)irb(main):144:2> puts "S: #{s} Y: #{y}"irb(main):145:2> endirb(main):146:1> end=> nilirb(main):147:0>t = Test.newirb(main):148:0> t.send(:method1, 23, 12)S: 23 Y: 12=> nil
清單 16. 訪問類私有方法
?
irb(main):258:0> class SendTestirb(main):259:1> privateirb(main):260:1> def helloirb(main):261:2> puts "Saying Hello privately"irb(main):262:2> endirb(main):263:1> end=> nilirb(main):264:0> y = SendTest.new=> #< SendTest:0x2cc52c0>irb(main):265:0> y.helloNoMethodError: private method `hello' called for #< SendTest:0x2cc52c0> from (irb):265irb(main):266:0> y.send(:hello)Saying Hello privately=> nil
如果您像我一樣具有 C++ 工作背景,且試圖編寫異常安全代碼,那么在看到 Ruby 有 throw 和 catch 關鍵詞時會開始感到異常親切。遺憾的是,throw 和 catch 在 Ruby 中的含義完全不同。
Ruby 通常使用 begin…rescue 塊處理異常。清單 17 提供了一個示例。
清單 17. Ruby 中的異常處理
?
begin f = File.open("ruby.txt") # .. continue file processingrescue ex => Exception # .. handle errors, if anyensure f.close unless f.nil? # always execute the code in ensure blockend
Ruby 中的 catch 和 throw 代碼塊實際上不是異常處理:您可以使用 throw 修改程序流程。清單 18 顯示了一個使用 throw 和 catch的示例。
清單 18. Ruby 中的 Throw 和 catch
?
irb(main):185:0> catch :label doirb(main):186:1* puts "This will print"irb(main):187:1> throw :labelirb(main):188:1> puts "This will not print"irb(main):189:1> endThis will print=> nil
有些人甚至說,Ruby 中對 catch 和 throw 的支持將 C goto 行為帶到一個全新的高度。鑒于函數可以有多個嵌套層,而 catch 塊可能在每一級,goto 行為類比似乎有據可循。
?清單 19. Ruby 中的異常處理:嵌套的 catch 塊
?
irb(main):190:0> catch :label doirb(main):191:1* catch :label1 doirb(main):192:2* puts "This will print"irb(main):193:2> throw :labelirb(main):194:2> puts "This won't print"irb(main):195:2> endirb(main):196:1> puts "Neither will this print"irb(main):197:1> endThis will print=> nil
Ruby 版本 1.8.7 不支持真正的并發性。確實不支持。但是您會說,在 Ruby 中有 Thread 構造函數。您說的沒錯。不過這個Thread.new 不會在您每次調用同一方法時生成一個真實的操作系統線程。Ruby 支持的是綠色線程:Ruby 解釋器使用單一操作系統線程來處理來自多個應用程序級線程的工作負載。
當某個線程等待一些輸入/輸出發生時,這一 “綠色線程” 概念很有用,而且您可以輕松調度一個不同的 Ruby 線程來充分利用 CPU。但是這一構造函數無法使用現代的多核 CPU(維基百科提供了一段內容,很好地解釋了什么是綠色線程。參見 參考資料 獲取鏈接)。
最后這一個示例(參見 清單 20)證明了這一點。
清單 20. Ruby 中的多個線程
#!/usr/bin/env ruby def func(id, count) i = 0; while (i < count) puts "Thread #{i} Time: #{Time.now}" sleep(1) i = i + 1 endend puts "Started at #{Time.now}"thread1 = Thread.new{func(1, 100)}thread2 = Thread.new{func(2, 100)}thread3 = Thread.new{func(3, 100)}thread4 = Thread.new{func(4, 100)} thread1.jointhread2.jointhread3.jointhread4.joinputs "Ending at #{Time.now}"
假設您的 Linux? 或 UNIX? 機器上擁有 top 實用程序,在終端運行代碼,獲取進程 ID,然后再運行 top –p <process id>。top啟動后,按住 Shift-H 來列出運行中線程的數量。您應當只能看到一個線程,確認了這一點:Ruby 1.8.7 中的并發性不過是個神話。
總的看來,綠色線程沒有什么壞處。它們在重負荷輸入/輸出密集型程序中仍然有用,更不用說該方法可能是操作系統間最可移植的一個了。
?
結束語
本文涵蓋了以下多個方面:
● Ruby 中類層次結構的概念
● 單例方法
● 解釋 self 關鍵詞和 method_missing 方法
● 異常
● 線程
盡管 Ruby 不乏特立獨行之處,但是使用它進行編程還是挺有趣的,而且其以最少的代碼完成大量工作的能力還是很強大的。難怪 Twitter 這樣的大型應用程序會使用 Ruby 來駕馭其真正的潛力。祝您有個快樂的 Ruby 編程體驗!
雖然Ruby是獨一無二的,但它在編程中的使用是很有趣的,而且它能夠用最少的代碼做大量的工作,這是非常強大的,武林技術頻道希望能得到大家的繼續支持。
新聞熱點
疑難解答
圖片精選