?
本文檔使用 PHP中文網(wǎng)手冊 發(fā)布
(與C等語言不同的是)Ruby的控制結(jié)構(gòu)是表達(dá)式,其中的一部分還會返回值(也有不返回值的,若把這些不返回值的表達(dá)式放在賦值表達(dá)式右邊的話,就會引發(fā) parse error)。
Ruby中包括從C和Perl那里繼承來的控制結(jié)構(gòu),還包括一種可以將控制結(jié)構(gòu)抽象化的功能,即 帶塊的方法調(diào)用。帶塊的方法調(diào)用使類的設(shè)計者可以自己定義一些包括循環(huán)在內(nèi)的控制結(jié)構(gòu)。
例:
if age >= 12 then print "adult fee\n" else print "child fee\n" end gender = if foo.gender == "male" then "male" else "female" end
語法:
if 表達(dá)式 [then] 表達(dá)式 ... [elsif 表達(dá)式 [then] 表達(dá)式 ... ] ... [else 表達(dá)式 ... ] end
若條件表達(dá)式的計算結(jié)果為真時,將計算then以下的表達(dá)式。若if的條件表達(dá)式為偽時,將計算elsif的條件部分??梢源嬖谌舾蓚€elsif部分,若所有的if以及elsif的條件表達(dá)式都為偽的話,如果有else部分,就計算它的表達(dá)式。
if
表達(dá)式的結(jié)果取決于條件成立部分(或else部分)中最后被計算的表達(dá)式的結(jié)果。若沒有else部分,且所有條件均不成立的話,就返回nil。
Ruby中只有false
和nil
代表偽,其他都是真,甚至0或空字符串也是如此。
請注意,在Ruby中,和if對應(yīng)的是elsif
,而并非else if
(C的語句)或者elif
(sh的語句)。
另外,當(dāng)if
條件表達(dá)式中出現(xiàn)正則表達(dá)式字面值時,將作如下處理
$_ =~ 字面值
例:
print "debug\n" if $DEBUG
語法:
表達(dá)式 if 表達(dá)式
當(dāng)右邊的條件成立時,計算左邊的表達(dá)式,并返回其結(jié)果。若條件不成立則返回nil。
例:
unless baby? feed_meat else feed_milk end
語法:
unless 表達(dá)式 [then] 表達(dá)式 ... [else 表達(dá)式 ... ] end
unless
與if
相反,當(dāng)條件表達(dá)式結(jié)果為偽時,才計算then后面的表達(dá)式。unless表達(dá)式中不能安插elsif語句。
例:
print "stop\n" unless valid(passwd)
語法:
表達(dá)式 unless 表達(dá)式
當(dāng)右邊的條件不成立時,計算左邊的表達(dá)式,并返回其結(jié)果。若條件不成立時返回nil。
例:
case $age when 0 .. 2 "baby" when 3 .. 6 "little child" when 7 .. 12 "child" when 13 .. 18 "youth" else "adult" end
語法:
case [表達(dá)式] [when 表達(dá)式 [, 表達(dá)式] ...[, `*' 表達(dá)式] [then] 表達(dá)式..].. [when `*' 表達(dá)式 [then] 表達(dá)式..].. [else 表達(dá)式..] end
case
先對一個表達(dá)式進(jìn)行匹配判斷,然后根據(jù)匹配結(jié)果進(jìn)行分支選擇。它使用"==="操作符比較when的指定值和最初那個表達(dá)式的計算值,若一致的話就計算when部分的內(nèi)容。
也就是說
case 表達(dá)式0 when 表達(dá)式1, 表達(dá)式2 stmt1 when 表達(dá)式3, 表達(dá)式4 stmt2 else stmt3 end
基本上等同于下面的if表達(dá)式。
_tmp = 表達(dá)式0 if 表達(dá)式1 === _tmp or 表達(dá)式2 === _tmp stmt1 elsif 表達(dá)式3 === _tmp or 表達(dá)式4 === _tmp stmt2 else stmt3 end
when 部分的計算順序同上面這個if句的計算順序是相同的。即從上到下(從左到右)地計算"==="。另外,“表達(dá)式0”只計算一次。
若when 部分中的最后一個表達(dá)式前帶"*"的話,該表達(dá)式將被當(dāng)作數(shù)組展開。
ary = [1,2,3] case v when *ary .. end
等同于
case v when 1, 2, 3 .. end
請參考描述各個類中"==="方法技術(shù)細(xì)節(jié)的文檔,來了解"==="在何種條件下為真。
當(dāng)case
的“表達(dá)式”部分被省略時,將計算第一個when條件部分為真的表達(dá)式。
foo = false bar = true quu = false case when foo then puts 'foo is true' when bar then puts 'bar is true' when quu then puts 'quu is true' end # 顯示 "bar is true"
case
將返回條件成立的when部分(或else部分)中最后被計算的表達(dá)式的結(jié)果。若所有條件都不成立的話,則返回nil。
例:
ary = [0,2,4,8,16,32,64,128,256,512,1024] i = 0 while i < ary.length print ary[i] i += 1 end
語法:
while 表達(dá)式 [do] ... end
只要表達(dá)式的計算值為真,就循環(huán)執(zhí)行while語句中的內(nèi)容。while不返回值。
ruby 1.7 特性: while
返回nil
。另外,可以使用帶參數(shù)的break,將while表達(dá)式的返回值設(shè)為那個參數(shù)的值。
例:
sleep(60) while io_not_ready?
語法:
表達(dá)式 while 表達(dá)式
只要右邊表達(dá)式的計算值為真,就循環(huán)執(zhí)行左邊部分。
若左邊表達(dá)式是begin,且即不含rescue,又不含ensure的話,則只在開始時計算一次然后就執(zhí)行循環(huán)。
ruby 1.7 特性: 在version 1.7中,即使出現(xiàn)rescue/ensure部分也會作相同處理。
例:
send_request(data) begin res = get_response() end while res == 'Continue'
while
修飾的表達(dá)式?jīng)]有返回值。
ruby 1.7 特性: while
修飾的表達(dá)式返回nil。另外,可以使用帶參數(shù)的break,將while修飾的表達(dá)式的返回值設(shè)為那個參數(shù)的值。
例:
until f.eof? print f.gets end
語法:
until 表達(dá)式 [do] ... end
在表達(dá)式的計算值變?yōu)檎嬷埃恢毖h(huán)執(zhí)行until中的內(nèi)容。until不返回值。
ruby 1.7 特性: until
返回 nil
。另外,可以使用帶參數(shù)的break,將until表達(dá)式的返回值設(shè)定為那個參數(shù)的值。
例:
print(f.gets) until f.eof?
語法:
表達(dá)式 until 表達(dá)式
在右邊表達(dá)式的計算值變?yōu)檎嬷?,一直循環(huán)執(zhí)行左邊部分。
若左邊表達(dá)式是begin,且即不含rescue,又不含ensure的話,則只在開始時計算一次然后就執(zhí)行循環(huán)。
ruby 1.7 特性: 在version 1.7中,即使出現(xiàn)rescue/ensure部分也會作相同處理
例:
send_request(data) begin res = get_response() end until res == 'OK'
until
修飾的表達(dá)式?jīng)]有返回值。
ruby 1.7 特性: until
修飾的表達(dá)式返回nil。另外,可以使用帶參數(shù)的break,將until修飾的表達(dá)式的返回值設(shè)為那個參數(shù)的值。
例:
for i in [1, 2, 3] print i*2, "\n" end
語法:
for lhs ... in 表達(dá)式 [do] 表達(dá)式.. end
先計算表達(dá)式得到一個對象,然后分別針對該對象中的每個要素,循環(huán)執(zhí)行for的內(nèi)容。這基本等價于
(表達(dá)式).each `{' `|' lhs..`|' 表達(dá)式.. `}'
之所以說“基本”是因為,do...end以及由{}構(gòu)成的塊中導(dǎo)入了新的局部變量的有效范圍,而for語句對于局部變量的有效范圍沒有任何影響。
for
將返回in
所指對象的each方法的返回值。
若想使用多個循環(huán)變量的話,可以這樣
for i,j in [[1,2], [3,4], [5,6]] p [i,j] end => [1, 2] [3, 4] [5, 6]
使用for或each時,每次只能取一個數(shù)組元素進(jìn)行循環(huán),而不能一次取多個。
for i,j in [1, 2, 3] p [i,j] end => [1, nil] [2, nil] [3, nil] # 可能您希望這樣[1,2] [3,nil],但實際上這是行不通的
您必須自己定義這樣的方法(迭代器)。請參考 each。
class Array def each2 i = 0 while i < self.size yield self[i], self[i+1] i += 2 end end end
例:
i = 0 while i < 3 print i, "\n" break end
語法:
break break val ruby 1.7 特性
break
將退出最內(nèi)層的循環(huán)。所謂循環(huán)是指,下列之一
與C語言不同,break只能從循環(huán)中退出,而不能從case中退出。
若使用break退出for或迭代循環(huán)后,該循環(huán)將返回nil。ruby 1.7 特性:但如果使用了參數(shù)的話,循環(huán)將返回那個參數(shù)的值。
例:
# 忽略空行的cat ARGF.each_line do |line| next if line.strip.empty? print line end
語法:
next next val ruby 1.7 特性
next
將跳轉(zhuǎn)到最內(nèi)側(cè)循環(huán)的頭部。在迭代器中,它將跳離yield調(diào)用。
使用next
跳離yield后,yield表達(dá)式將返回nil。ruby 1.7 特性:但如果使用了參數(shù)的話,yield表達(dá)式的返回值就是該參數(shù)的值。
例:
redo
語法:
redo
不檢查循環(huán)條件,重新開始當(dāng)前循環(huán)。
例:
retry
語法:
retry
在迭代、塊或for語句中使用retry,意味著重啟迭代器。同時迭代器的參數(shù)也將被重新計算。
for i in 1..5 retry if some_condition # 從 i == 1 開始重新執(zhí)行 end # 用戶定義的 "until循環(huán)" def UNTIL(cond) return if cond yield retry end
除了循環(huán)以外,還可以在rescue部分(后述)中使用retry
。這時將從begin表達(dá)式開始重新執(zhí)行。使用retry可以在某處理過程成功之前,一直循環(huán)該處理過程。
begin do_something # exception raised rescue # handles error retry # restart from beginning end
若在rescue部分、迭代器塊或for語句之外使用retry的話會引發(fā)LocalJumpError異常。
歸納一下,在迭代調(diào)用中使用break
, next
, redo
, retry
的作用如下。
def iter (a) : (b) yield (c) : (d) end iter { retry } -> 跳到 (a) iter { redo } -> 跳到 (b) iter { next } -> 跳到 (c) iter { break } -> 跳到 (d)
嚴(yán)格地講(a)是從計算參數(shù)開始的。(b)指的是即將開始執(zhí)行塊的時候(yield的參數(shù)不會被再次計算)。(d)指的是方法的終結(jié)。
def iter(var = p("(a)")) p " : " yield p "(c)" p " : " ensure p "(d)" end iter { p "(b)"; retry } # => (a) .. (b)(d)(a) .. (b)(d)(a) ... iter { p "(b)"; redo } # => (a) .. (b)(b)(b)(b) ... iter { p "(b)"; next } # => (a) .. (b)(c) .. (d) iter { p "(b)"; break } # => (a)..(b)(d)
例:
raise "you lose" # 引發(fā)RuntimeError異常 # 下面兩個將引發(fā)SyntaxError異常 raise SyntaxError, "invalid syntax" raise SyntaxError.new("invalid syntax") raise # 再次引發(fā)上一個異常
語法:
raise raise message或exception raise error_type, message raise error_type, message, traceback
引發(fā)異常。第一句將再次引發(fā)上一個異常。第二句中,若參數(shù)是字符串的話,就把它當(dāng)作錯誤信息(message)再引發(fā)RuntimeError異常。若參數(shù)為異常對象則引發(fā)該異常。第三句中,將引發(fā)第一個參數(shù)所指的異常,并以第二個參數(shù)的內(nèi)容作為錯誤信息。第四句中,第三參數(shù)裝載的是源自于$@或caller的堆棧信息,它指明發(fā)生異常的地點。
可以使用begin表達(dá)式的rescue部分來捕捉異常。這時使用rescue error_type => var就可以得到異常對象。您還可以從內(nèi)部變量$!中獲得這個對象。另外,變量$@中裝載的是發(fā)生異常的源代碼位置。
raise并不是Ruby的保留字,它是Kernel模塊中定義的函數(shù)式的方法。
例:
begin do_something rescue recover ensure must_to_do end
語法:
begin 表達(dá)式.. [rescue [error_type,..] [=> evar] [then] 表達(dá)式..].. [else 表達(dá)式..] [ensure 表達(dá)式..] end
若給出了rescue部分(可以有若干個)的話,就可以在發(fā)生異常時捕捉到它。若存在與異常類型一致的rescue部分的話,就執(zhí)行rescue的內(nèi)容??梢允褂?font color="blue">$!來查看異常的情況。另外,若事先設(shè)定了變量evar的話,它也可以像$!一樣存儲那些異常的信息。
begin raise "error message" rescue => evar p $! p evar end # => #<RuntimeError: error message> #<RuntimeError: error message>
rescue部分使用Object#kind of?來判斷剛才的異常的類是否就是自己期待的異常類,或者這二者是否處于父類/子類的關(guān)系。
若error_type被省略,則將捕捉StandardError的子類中的所有異常。Ruby的內(nèi)部異常(除了SystemExit和Interrupt這些退出命令以外)是StandardError的子類。
請參考異常類來了解異常類的層次關(guān)系。
在rescue
部分中,error_type與普通的參數(shù)一樣接受計算,若符合的話就執(zhí)行相應(yīng)部分的內(nèi)容。若error_type的計算值既非類又非模塊的話,則引發(fā)TypeError異常。
若運行過程中沒發(fā)生異常,則開始計算可選的else部分。
若存在ensure
部分的話,則在begin表達(dá)式結(jié)束之前一定會計算它。
begin
表達(dá)式整體的計算值取決于,begin的內(nèi)容部分/rescue部分/else部分中最后被計算的句子的值。若各部分中均無語句時,其值為nil。不管怎樣,ensure部分的值始終會被忽略。
例:
open("nonexistent file") rescue STDERR.puts "Warning: #$!"
語法:
表達(dá)式1 rescue 表達(dá)式2
若表達(dá)式1中發(fā)生異常時就計算表達(dá)式2。這等同于下例。不能指定想捕捉的異常類。(也就是說,只能捕捉StandardError異常類的子類了)
begin 表達(dá)式1 rescue 表達(dá)式2 end
在包括rescue修飾句的表達(dá)式中,若沒發(fā)生異常則返回表達(dá)式1的值,若發(fā)生異常則返回表達(dá)式2的值。但在大多數(shù)場合中,因為考慮到優(yōu)先度的問題,所以需要使用括號將整個表達(dá)式括起來。
var = open("nonexistent file") rescue false p var => nil # 因為只定義了一個空變量var var = (open("nonexistent file") rescue false) p var => false
特別是傳遞給某方法的參數(shù)時,有必要使用雙重括號。
p(open("nonexistent file") rescue false) => parse error p((open("nonexistent file") rescue false)) => false
ruby 1.7 特性: 在1.7中,rescue的優(yōu)先度發(fā)生了變化,因此免去了這些煩惱。
var = open("nonexistent file") rescue false p var => false p(open("nonexistent file") rescue false) => false
例:
return return 12 return 1,2,3
語法:
return [表達(dá)式[`,' 表達(dá)式 ... ]]
結(jié)束方法的運行,且把表達(dá)式的值設(shè)定為方法的返回值。若給出了2個以上的表達(dá)式,則將把這些表達(dá)式化為一個數(shù)組,然后把該數(shù)組設(shè)定為方法的返回值。若省略表達(dá)式,將返回值設(shè)為nil。
例:
BEGIN { ... }
語法:
BEGIN '{' 語句.. '}'
注冊初始化例程(routine)。BEGIN塊所指定的語句的執(zhí)行順序?qū)⑾扔谠撐募腥魏握Z句。若有多個BEGIN塊的話,將按照出現(xiàn)順序依次執(zhí)行。
BEGIN塊在編譯時被注冊。也就是說,同一條語句只會被注冊一次。
if false BEGIN { p "begin" } end # => "begin"
BEGIN塊引入了獨立的局部變量作用域,因此不能和外部共享局部變量。為了與塊外交換信息,必須借助于常數(shù)或全局變量。
BEGIN { $foo, foo = true, true } p $foo # => true p foo # undefined local variable or method `foo' for main:Object (NameError)
BEGIN不能出現(xiàn)在方法定義表達(dá)式中,否則會引發(fā) parse error。
def foo BEGIN { p "begin" } end # => -:2: BEGIN in method
例:
END { ... }
語法:
END '{' 語句.. '}'
注冊“善后”例程。END塊中指定的語句會在解釋器結(jié)束前得到執(zhí)行。關(guān)于Ruby退出程序時的相關(guān)處理問題,請參考結(jié)束時的相關(guān)處理。
若注冊了若干END塊的話,則以與注冊時相反的順序依次執(zhí)行這些塊。
END { p 1 } END { p 2 } END { p 3 } # => 3 2 1
END塊中同一條語句只會執(zhí)行一次。如下例,即使把END塊置入循環(huán)中,也只會注冊一次。若想實現(xiàn)復(fù)用,請使用 at_exit。
5.times do |i| END { p i } end # => 0
若把END塊置入方法定義表達(dá)式中會引起警告。若有意如此,請使用at_exit。
def foo END { p "end" } end p foo # => -:2: warning: END in method; use at_exit nil "end"
END與BEGIN不同的是,它在運行時進(jìn)行注冊。因此,下例中的END塊將不會運行。
if false END { p "end" } end
END和at_exit中注冊的善后處理無法取消。
END塊與BEGIN塊不同的是,它同周圍部分共享作用域。也就是說,它的作用域同迭代器一樣。
若END塊中發(fā)生了異常,將中斷該塊。但解釋器并不結(jié)束,只是發(fā)出信息,并且試圖處理完所有的善后例程。
例:
END { p "FOO" } END { raise "bar"; p "BAR" } END { raise "baz"; p "BAZ" } => baz (RuntimeError) bar (RuntimeError) "FOO"