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