從"Hello,World"開始 快速入門Ruby

 

  最簡單的"Hello,World"程序開始逐步深入了解Ruby的類、block、迭代器等特征

  這是一個短小的Ruby入門。這里假設讀者已經安裝了Ruby,如果你沒有安裝的話,請在閱讀文前訪問Ruby官方網站進行下載并安裝。




  交互式的Ruby

  打開IRB(交互式Ruby外殼):

·    如果你使用Mac OS X,那么請打開終端窗口輸入irb;

·    如果你使用Linux,那么請打開shell輸入irb;

·    如果你使用windows,那么請在開始菜單中找到Ruby->fxri,并執行它。




  Ok,在打開IRB之后,在其中輸入"Hello World"。



 

Ruby聽從你的安排!

  發生了什么?我們剛才編寫了世界上最短小的“Hello World”程序嗎?這么說不太確切。第二行輸出是IRB告訴我們:上一個表達式的評估結果。如果我們希望打印出“Hello World”,那么就還需要一點努力:





  puts在Ruby中是一個簡單的打印輸出命令。后面的“=> nil”表示什么?——那是表達式的結果。Puts總是返回nil,這是Ruby中表示“絕對無值”(absolutely-positively-nothing value)的方式,看上去有些類似Java中的null。

你的免費計算器在這里!

  無需做什么,我們就能把IRB作為一個簡單的計算器使用:



  這樣就能計算3+2。夠簡單的!那么3乘以2如何?你可以在下面繼續輸入3*2,也可以回到上面(3+2處)重新修改你剛剛輸入的計算公式。使用鍵盤上的向上鍵,使光標到達3+2那一行,再用左鍵移動光標到加號上,然后使用空格鍵進行修改。



  下面,讓我們嘗試計算3的平方:




  在Ruby語言中,**表示冪運算。那么如何計算平方根呢?



  Ok,等一下,表達式中的sqrt(9)表示什么?你一定能猜到這是計算9的平方根。而Math表示什么?不要著急,下面就讓我們進一步了解像Math這樣的模塊。

  模塊——按照主題分組的代碼

  Math是Ruby內建的數學模塊。在Ruby中,模塊提供了兩種角色:一種角色是將類似的方法聚集在同一個“家族”名下。因此,Math也包括sin、tan這樣的方法。第二種角色是一個圓點(dot),它標記了消息的接收者。什么是消息?在上面的例子中,sqrt(9)便是消息,它意味著調用sqrt方法取出9的平方根。

  Sqrt方法調用的結果是3.0。你可能注意到它并不是3。這是因為多數情況下,數字的平方根并不是整數,所以這里返回了一個浮點數。

  那么我們如何記住這些計算結果呢?——將結果賦值給變量。




如何定義方法?

  如何才能方便省事地隨意輸出字符串,而無需過多地勞煩我們的手指呢?——我們需要定義一個方法!




  上面的代碼中第一行“def h”標志著方法定義的開始。它告訴Ruby我們正在定義一個名為h的方法。下面一行是方法體:puts "Hello World"。最后,也就是第三行“end”通知Ruby我們完成了方法定義。Ruby的回應“=> nil”告訴我們它已經知道我們定義了此方法。

  簡短、重復地調用方法

  現在,讓我們嘗試多次執行這個方法:



  哈,這太容易了。在Ruby中調用某個方法只需將方法名提交給Ruby。當然,這是在方法沒有參數的情況下。如果你愿意也可以添加一個空白的括號,但是這沒有必要。

  如果我們想對某個人說hello而不是整個“世界”(world),那該怎么做?——重定義h方法使它接收name參數。



字符串中的奧秘

  “#{name}”是什么意思?這是Ruby在某個字符串中插入其它字符的方式。在大括號之間放入的字符串(這里是指name)將被外部的字符串代替。你也可以使用字符串類內建的capitalize方法來確保某人名字的首字母大寫:




  上面的代碼有兩個地方需要說明:

  第一,我們通過無括號的方式調用方法,因為括號是可選的;

  第二,這里的默認參數值為“World”。也就是說在調用方法時如果沒有提供name參數,則使用默認值“World”。

  進化為Greeter!

  我們是否需要一個真正的問候者(greeter),他能記住你的名字、問候你、總是尊重地向你示好?那么這就最好建立一個“Greeter”類:



在上面的類代碼中定義了一個稱為Greeter的類和一些類方法,其中出現了一些新的“關鍵詞”:請注意“@name”,它是類的實例變量,并對類中的所有方法(say_hi和say_bye方法)都有效。

  如何讓Greeter類發揮作用?現在讓我們來建立一個Greeter對象并使用它!




  Greeter類的實例對象g被建立后,它便接受了name參數(值為Pat)。那么我們能直接訪問name嗎?



  看看上面的編譯錯誤來看,這樣直接訪問name是行不通的。

  窺視對象的內部

  對象中的實例變量總是隱藏于其中,但也并非毫無蹤跡可尋,通過審查(inspect)對象便會見到它們。當然還有其它的訪問方法,但是Ruby采用了良好的面向對象的方式來保持數據的隱藏性。



喔!這么多方法,可是我們只定義了兩個方法呀?其它的方法又出自何處?不要擔心,instance_methods方法列出了Greeter對象的所有方法,其中包括父類中定義的方法。如果我們只想對Greeter類的方法進行列表的話,那么把false作為參數調用instance_methods方法即可。false意味著我們不需要父類定義的方法。



 

  哈哈,這才是我們想要的。下面讓我們看看Greeter對象能回應哪些方法:

 

  它知道say_hi、to_s(此方法將對象轉換為字符串,是任何對象都必備的默認方法,很想Java中的toString方法),但它不知道name。

  隨時修改類定義

  如何才能查看或者修改name呢?Ruby提供了訪問對象變量的簡單方法:



  在Ruby語言中,你能夠多次打開某個類并修改它。而修改所帶來的變化將應用在此后建立的任何新對象中、甚至現存的此類對象中。下面讓我們建立一個新對象并訪問它的@name屬性。



 

  我們通過使用attr_accessor定義了兩個方法:

·                                   “.name”用來獲取name屬性值;

·                                   “.name=”用來設置namee屬性值。

  這很類似在Java類中訪問被Public修飾的成員變量。

向每個人問候,MegaGreeter不會漏掉一個人

  Greeter并不完美,因為它只能一次服務一個人。所以我們在這里設計一個能夠一次向全世界、世界上每個人或者在名單中的人發送問候的MegaGreeter類。在這里,我們將放棄從前的IRB交互模式,轉而改為編寫Ruby程序文件。

  退出IRB的方法:輸入“quit”、“exit”或者按下Control+D的組合鍵。



保存上面的代碼到名為“ri20min.rb”的文件中,并使用“ruby ri20min.rb”的命令執行它。程序輸出如下:





  下面我們將深入了解一下上面的代碼。

  請注意上面代碼中的起始行,它以#開頭。在Ruby語言中,任何以#開頭的行都被視為注釋,并被解釋程序忽略。

  我們的say_hi方法已經發生了變化:




  它查找@names參數并按照其參數值作出決定:

  如果參數值為nil,它將打印三個圓點。

  那么@names.respond_to?("each")表示什么?

循環——也叫迭代

  如果@names對象具有each方法,那么它是可以被迭代的,進而可以對其進行迭代,從而問候列表中每個人。如果@names不具備each方法,則將它自動轉換為字符串,并執行默認的問候。



  each是一種方法,它接受一個代碼塊(block of code),然后針對列表中的每個成員執行這個代碼塊,而在do和end之間的部分便是這個非常類似匿名函數的代碼塊。在管道符之間的變量是代碼塊的參數name,它作為代碼塊參數被綁定為列表成員,而代碼塊puts "Hello #{name}!"將使用這個參數進行輸出。

  大多數其它的編程語言使用循環遍歷列表,下面是C語言的循環示例:




  上面的代碼顯然可以工作,但它不夠“優雅”!你不得不用i這個多余的循環變量,還需要指出列表的長度,然后再解釋如何遍歷列表。

  Ruby的迭代方式則更加優雅,所有的內部管理細節都隱藏在each方法中,你所需做的就是告訴它如何處理其中的每個成員。

  塊(block),Ruby邊緣的高亮點!

  塊(block)的真正優勢在于:能夠處理比列表更加復雜的對象。除了在方法中可以處理簡單的內部管理細節外,你還能處理setup、teardown和所有錯誤,而不讓用戶有所察覺。




  say_bye方法沒有使用each,而是檢查@names是否具有join方法,如果具有join方法,則調用join方法。否則它將直接打印@names變量。

  此方法并不關心變量的實際類型,這依賴于它所支持的那些被稱為“Duck Typing”的方法:duck typing是動態類型的一種形式:變量的值自身隱含地決定了了變量的行為。這暗示了某個對象與其它實現了相同接口的對象之間是可交換的,不管對象之間是否具有繼承關系。鴨子測試(duck test)是對duck typing的一種形象比喻——“如果它走路像鴨子,那么也一定像鴨子一樣呷呷地叫,那么它必定是一只鴨子”。duck typing是某些編程語言的特性:如Smalltalk, Python, Ruby, ColdFusion。

  Duck Typing的益處是無需對變量的類型進行嚴格地限制,如果某人使用一種新類型的列表類,只要它實現了與其它列表相同語義的join方法,便可以拿來使用。

啟動腳本

  文件上半部分是MegaGreeter類的代碼,而后面剩下的部分則是對這些類方法的調用。而這是我們最后值得注意的一點:




  __FILE__是一個“具有魔力”的變量,它代表了當前文件名。$0是用于啟動程序的文件名。那么代碼“if __FILE__ == $0”便意味著檢查此文件是否為將被使用的主程序文件。這樣做可以使程序文件作為代碼庫使用,而不是可執行代碼;但當此文件被用作執行文件時,也可被執行。

  如何進一步學習Ruby

  到此便是本入門的尾聲了。當然還有許多值得瀏覽的:Ruby提供的各種不同的控制結構;塊和yield的使用;模塊作為mixins使用等。希望這次Ruby初體驗能使你對Ruby更感興趣。

  注:mixin在面向對象編程語言中是一種提供某些功能給子類繼承的類,但mixin并不能實例化。從某個mixin繼承并不是什么特殊的形式,而它更適于收集功能。某個子類甚至可以通過繼承一個或者多個mixin選擇繼承它的全部或者多數功能。一個mixin能延期定義和綁定方法直到運行時,而屬性和實例參數也將在編譯時才被定義。這不同于多數常見的方式:定義所有的屬性、方法,并在編譯時進行初始化。

  如果這樣的話,請埋頭翻閱我們的文檔,那里有免費、豐富的在線手冊和入門資源。或者如果你喜歡在啃書本的話,可以到圖書列表中選擇一些你所需要的。

版權聲明:需Matrix授權發布,如需轉載請聯系Matrix


調試過的代碼:

irb(main):001:0> "Hello World"

=> "Hello World"

irb(main):002:0> puts "hello world"

hello world

=> nil

irb(main):003:0> 3+2

=> 5

irb(main):004:0> def h

irb(main):005:1>   puts "hello world"

irb(main):006:1>   end

=> nil

irb(main):007:0> h

hello world

=> nil

irb(main):008:0> h()

hello world

=> nil

irb(main):009:0> def h(name)

irb(main):010:1>   puts "hello #{name}!"

irb(main):011:1>   end

=> nil

irb(main):012:0> h("keywen")

hello keywen!

=> nil

irb(main):013:0> def h(name = "keywen")

irb(main):014:1>   puts "hello #{name.capitalize}"

irb(main):015:1>   end

=> nil

irb(main):016:0> h"wen"

hello Wen

=> nil

irb(main):017:0> h

hello Keywen

=> nil

irb(main):018:0> class Greeter

irb(main):019:1>   def initialize (name ="keywen")

irb(main):020:2>     @name = name

irb(main):021:2>     end

irb(main):022:1>   def say_hi

irb(main):023:2>     puts "Hi #{@name}!"

irb(main):024:2>     end

irb(main):025:1>   def say_bye

irb(main):026:2>     puts "Bye #{@name},come back soon."

irb(main):027:2>     end

irb(main):028:1>   end

=> nil

irb(main):029:0> g = Greeter.new("Pat")

=> #<Greeter:0x7237aec @name="Pat">

irb(main):031:0> g.say_hi

Hi Pat!

=> nil

irb(main):032:0> g.say_bye

Bye Pat,come back soon.

=> nil

irb(main):034:0> Greeter.instance_methods

=> ["inspect", "clone", "public_methods", "display", "instance_variable_defined?", "equal?", "freeze", "methods", "respond_to?", "dup", "to_yaml_style", "instance_variables", "__id__", "eql?", "method", "pretty_print_inspect", "say_hi", "id", "send", "singleton_methods", "taint", "to_yaml_properties", "instance_variable_get", "frozen?", "instance_of?", "__send__", "to_a", "say_bye", "to_yaml", "type", "require_gem", "object_id", "instance_eval", "protected_methods", "require", "==", "h", "===", "taguri", "pretty_print_instance_variables", "instance_variable_set", "extend", "kind_of?", "pretty_print_cycle", "to_s", "gem", "taguri=", "class", "hash", "private_methods", "=~", "tainted?", "untaint", "nil?", "pretty_inspect", "is_a?", "pretty_print"]

irb(main):035:0> Greeter.instance_methods(false)

=> ["say_bye", "say_hi"]

irb(main):038:0> g.respond_to?("name")

=> false

irb(main):039:0> g.respond_to?("say_bye")

=> true

irb(main):040:0> g

=> #<Greeter:0x7237aec @name="Pat">

irb(main):041:0> class Greeter

irb(main):042:1>   attr_accessor:name

irb(main):043:1>   end

=> nil

irb(main):047:0> g = Greeter.new("keywen")

=> #<Greeter:0x71cef74 @name="keywen">

irb(main):048:0> g.respond_to?("name")

=> true

irb(main):049:0> g.respond_to?("name=")

=> true

irb(main):050:0> g.say_hi

Hi keywen!

=> nil

irb(main):051:0> g.name="wen"

=> "wen"

irb(main):052:0> g.say_hi

Hi wen!

=> nil

irb(main):053:0>