st = "\033[7m"
en = "\033[m"
puts "Enter an empty string at any time to exit."
while true
end
陣列可用 join
轉換為字串,字串可用 split
轉換為陣列:
ruby> str.split(":")
02 secret = words[rand(3)]
03
04 print "guess?"
05 while guess = STDIN.gets
06
07
08
09
10
11
12
13
14 end
15 puts "the word is "+ secret + " . "
程式中,使用了 while
這個新的控制結構。若指定的條件為真時,就會重複運行 while
與其對應的 end
之間的程式碼。這個範例中,guess=STDIN.gets
既是一個有動作的敘述(收集使用者輸入的一行內容,並儲存為 guess
),也是一項判斷條件(若沒有輸入任何內容,guess
即等於整個 guess=STDIN.gets
表示式的值,而這個值是 nil
,會讓 while
停止迴圈。)
STDIN
是標準輸入 (standard input) 物件。一般來說,guess=gets
的功能與 guess=STDIN.gets
一樣。
第 2 行的 rand(3)
會傳回 0 至 2 間的一個亂數 ( random number), 用來提取陣列 words
中的一個元素。
第 5 行中,我們利用方法 STDIN.gets
,從標準輸入中提取一行內容。提取時若出現 EOF(檔案結尾),gets
會傳回 nil
。因此 while
程式碼會不斷重複,直到遇到代表結束輸入的 ^D(DOS/Windows 中則是 ^Z 或 F6)。
第 6 行中 guess.chop!
會刪除 guess
後的最後一個字元;在本例中這是一個換行 (newline) 字元,這是因為 gets
會包含使用者按下的 Return 鍵,這是我們不感興趣的。
在第 15 行中輸出謎底。我們將謎底寫為三個字串將之相加在一起;這與將 secret
寫為 #{secret}
的單一字串效果相同,清楚顯示這是將要計算的變數,而不只是逐字輸出而已:
==================================================================================
加入驚嘆號就是「破壞性的」(destructive) chop, 那兩者有甚麼差別呢?在 Ruby 中,我們一般在方法名稱後加上 '!
' 或 '?
'。驚嘆號(!
有時讀作 "bang!")代表具有破壞性,即會改變所接觸物件的值。chop!
會直接影響字串,但 chop
則會提供一個刪減後的版本,而不影響原本的物件。以下將闡釋兩者的差異。
ruby> s1.chop!
ruby> s2 = s1.chop
ruby> s1
你也會看見有 chomp
及 chomp!
。提供更多選擇性:字串結尾是一個換行字元時,結尾才會刪掉。例如:"XYZ".chomp!
,並不會有任何效果。記住兩者差別的技巧就是,想像人或動物吃東西前,總會先嘗嘗味道,才會一口咬下去,而不像斧頭那樣隨便就砍下去。
==================================================================================
case
內部使用關聯運算子 (relationship operator) ===
,同一時間檢查數個條件。為保持 Ruby 的物件導向特質,出現在 when
條件內的物件會以 ===
適當解釋。例如,以下程式碼會測試字串是否等於第一個 when
的字串,再測試是否符合第二個 when
的正規表示式。
includes /def/
Ruby 有個特別的變數稱為 self
,指向現在的物件(也就是呼叫此方法的物件)。因為經常使用 "self.
",所以在物件本身呼叫方法時,可以省略:
即等同於
我們可以把傳統的函數呼叫 (function call) 當做是物件 method,只是省略了 self
呼叫。因此 Ruby 可稱為純物件導向語言(編註:因為所有函式都在物件之中)。當然,這個函數呼叫與其他程式語言的函數非常類似,這也方便了那些不明白 Ruby 中函數呼叫就是物件方法的人。如果必要的話,我們會說 "函數 (functions)",以跟物件方法(object methods)做區別。
==================================================================================
實例 (instance) 的行為由其所屬的類別決定,但有時候我們知道某個實例應該具有特定行為。大部分語言中,我們必須大費周章定義其他類別,但只能實例化 (instantiate) 一次。而 Ruby 能為所有物件提供自己的方法。
ruby> test1 = SingletonTest.new
ruby> test2 = SingletonTest.new
ruby> def test2.size
ruby> test1.size
ruby> test2.size
本例中,test1
與 test2
屬於同一類別,但 test2
具有重新定義的 size
方法,因此兩者的行為會不一樣。只給予單一物件的方法稱為單件方法 (singleton method)。
單件方法經常用於圖形使用者介面 (graphic user interface, GUI) 的元素,當按下不同按鈕,就會執行不同動作。
單件方法並不是 Ruby 獨有的,CLOS、Dylan 等也有。有些語言例如 Self 及 NewtonScript 更只有單件方法。這有時會稱為原型 (prototype-based) 語言。
==================================================================================
下例中,defined?
是檢查識別符 (identifier) 有否定義的運算子。若已定義,就會傳回該識別符的描述,否則就傳回 nil
。如你所見,bar
的作用域位於迴圈內;若迴圈結束,bar
就變成未定義了。
44
ruby> loop{bar=45; puts bar; break}; defined?(bar)
45
簡單使用存取器
因為不少實例變數都需要存取器方法,因此 Ruby 提供了一些快捷的方式。
快捷方式 | 效果 |
attr_reader :v | def v; @v; end |
attr_writer :v | def v=(value); @v=value; end |
attr_accessor :v | attr_reader :v; attr_writer :v |
attr_accessor :v, :w | attr_accessor :v; attr_accessor :w |
==================================================================================
將假設 (assumption) 改為需求 (requirement)
有時候,預設值並不大合理, 例如有「水果預設種類」這種東西嗎?比較理想的是,要求創造水果時,即指定每件水果的種類。因此,我們要在 initialize
方法中加入一個形式引數 (formal argument), 供應至 new
的引數其實都傳遞至 initialize
。
ruby> f5 = Fruit.new "mango"
ruby> f6 = Fruit.new
ERR: (eval):1:in `initialize': wrong # of arguments(0 for 1)
組織你的程式碼
Ruby 具有高層級的動態性 (dynamism),指的是類別、模組、方法只會在定義它們的程式碼運作後才存在。如果你習慣使用較靜態的語言編寫程式的話,有時候可能會出現驚喜。
puts successor(3)
def successor(x)
end
雖然直譯器執行前,會檢查整份腳本文件的語法,但 def successor ... end
程式碼需要真的運作,才能建立 successor
方法。因此,排列腳本的次序將是關鍵。