Rubyでは(Cなどとは異なり)制御構造は式であって、何らかの値を 持ちます。 *1 *2 RubyはC言語やPerlから引き継いだ制御構造を持ちますが、 その他にイテレータというループ抽象化の機 能があります。イテレータは繰り返しを始めとする制御構造をユー ザが定義する事が出来るものです.
例:
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 式 [then] 式 ... [elsif 式 [then] 式 ... ] ... [else 式 ... ] end
条件式を評価した結果が真である時、then
以下の式を実行します。
if
の条件式が偽であれば elsif
の条件を評価します。
elsif
節は複数指定できます。全ての if
および elsif
の条件式が偽であったときは else
節があればその式が実行されます。
if
式は、条件が成立した節(あるいは else
節)の最後に実行し
た式の結果を返します。else
節がなくいずれの条件も成り立たなけれ
ば nil
を返します。
Ruby では false
または nil
だけが偽で、それ以外は 0 や空文
字列も含め全て真です。
Ruby では if を繋げるのは elsif
であり、else if
(C のように)でも elif
(sh のように)でもないことに注意してください。
また if
の条件式が正規表現のリテラルである時には
$_ =~ リテラル
であるかのように評価されます。
例:
print "debug\n" if $DEBUG
文法:
式 if 式
右辺の条件が成立する時に、左辺の式を評価してその結果を返します。
条件が成立しなければ nil
を返します。
例:
unless baby? feed_meat else feed_milk end
文法:
unless 式 [then] 式 ... [else 式 ... ] end
unless
は条件実行を行いますが、条件が if
と反対で、条件が偽の時に実行を行います。unless
式に
elsif
を指定することはできません。
例:
print "stop\n" unless valid(passwd)
文法:
式 unless 式
右辺の条件が成立しない時に、左辺の式を評価してその結果を返します。
条件が成立しなければ nil
を返します。
例:
case $age when 0 .. 2 "baby" when 3 .. 6 "little child" when 7 .. 12 "child" when 13 .. 18 "youth" else "adult" end
文法:
case [式] [when 式 [, 式] ... [then] 式..].. [else 式..] end
case
は一つの式に対する一致判定による分岐を行います。when
節で指定された値と最初の式を評価した結果とを演算子 ===
を用いて
比較して、一致する場合には when
節の本体を実行します。
つまり、
case 式0 when 式1, 式2 stmt1 when 式3, 式4 stmt2 else stmt3 end
は以下の if
式とほぼ等価です。
_tmp = 式0 if 式1 === _tmp || 式2 === _tmp stmt1 elsif 式3 === _tmp || 式4 === _tmp stmt2 else stmt3 end
===
がどのような条件で真になるかは、各クラスの ===
メソッ
ドの動作についてのドキュメントを参照して下さい。
case
の「式」を省略した場合、when
の条件式が偽でない最初の
式を評価します。
$bar = "true" case when $foo; p :foo when $bar; p :bar when $baz; p :baz end => :bar
case
は、条件が成立した when
節、(あるいは else
節)
の最後に実行した式の結果を返します。いずれの条件も成り立たなければ
nil
を返します。
例:
while sunshine work() end
文法:
while 式 [do] ... end
式を評価した値が真の間、本体を繰り返して実行します。
while
は、値を返しません。
例:
sleep while idle
文法:
式 while 式
右辺の式を評価した値が真の間、左辺を繰り返して実行します。
左辺の式が begin
である場合には、それを最低一度は
評価します。
例:
begin sleep end while idle
while
修飾した式は値を返しません。
例:
until sunrise sleep end
文法:
until 式 [do] ... end
式を評価した値が真になるまで、本体を繰り返して実行します。
until
は、値を返しません。
例:
work until tired
文法:
式 until 式
右辺の式を評価した値が真になるまで、左辺を繰り返して実行しま す。
左辺の式が begin
である場合には、左辺を最低一度は
評価します。
例:
begin sleep end until sunrise
until
修飾した式は値を返しません。
例:
for i in [1, 2, 3] print i*2, "\n" end
文法:
for lhs ... in 式 [do] 式.. end
式を評価した結果のオブジェクトの各要素に対して本体を繰り返し て実行します。これは以下の式とほぼ等価です。
(式).each `{' `|' lhs..`|' 式.. `}'
「ほぼ」というのは、do ... end
または{ }
による
ブロックは新しいローカル変数の有効範囲を導入するのに対し、
for
文はローカル変数のスコープに影響を及ぼさない点が
異なるからです。
for
は、in
に指定したオブジェクトの each
メソッドの戻り値を返します。
例:
i=0 while i<3 print i, "\n" break end
文法:
break
break
はもっとも内側のループを脱出します。ルー
プとは
のいずれかを指します。Cと違い、break
はループを
脱出する作用だけを持ち、case
を抜ける作用は持ち
ません。
break
によりループを抜けた、for
やイテレータは、nil
を返します。
例:
next
文法:
next
next
はもっとも内側のループの次の繰り返しにジャンプします。
イテレータでは、yield 呼出し
の脱出になります。
next
により抜けた yield
式は nil
を返します。
例:
redo
文法:
redo
ループ条件のチェックを行なわず、現在の繰り返しをやり直します。
例:
retry
文法:
retry
イテレータ、ブロックまたはfor文の中で使われた場合には、そのイテレータ を起動しなおします。イテレータの引数も再評価されます。
for i in 1..5 retry if some_condition # i == 1 からやり直し end # ユーザ定義の "untilループ" def UNTIL(cond) yield retry unless cond end
retry
は、ループ以外に後述の rescue
節でも使えます。この場
合は、begin
式を始めからもう一度実行します。retry
を使うこ
とである処理が成功するまで処理を繰り返すようなループを作ることができます。
begin do_something # exception raised rescue # handles error retry # restart from beginning end
rescue
節やイテレータ、ブロック for 文以外で retry
が用い
られた場合には例外 LocalJumpError が発生します。
*3
イテレータ呼び出しにおける break
, next
, redo
,
retry
をまとめると以下のようになります。
def iter (a) : (b) yield (c) : (d) end iter { retry } -> (a) へ飛ぶ iter { redo } -> (b) へ飛ぶ iter { next } -> (c) へ飛ぶ iter { break } -> (d) へ飛ぶ
(a) は、厳密には引数評価から始まります。(b) は yield 実行の直前を 指しています。(d) は、メソッドの終了です。
def iter(var = p("(a)")) p " : " # p "(b)" 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" # 例外 RuntimeError を発生させる # 以下の二つは SyntaxError を発生させる raise SyntaxError, "invalid syntax" raise SyntaxError.new("invalid syntax") raise # 最後の例外の再発生
文法:
raise raise messageまたはexception raise error_type, message raise error_type, message, traceback
例外を発生させます。第一の形式では直前の例外を再発生させます。 第二の形式では、引数が文字列であった場合、その文字列をメッセー ジとする RuntimeError 例外を発生させます。引数が例外 オブジェクトであった場合にはその例外を発生させます。第三の形式 では第一引数で指定された例外を、第二引数をメッセージとして発生さ せます。第四の形式の第三引数は $@または callerで得られる スタック情報で、例外が発生した場所を示します。
発生した例外は後述の begin
式の rescue
節で捕らえることができます。
その場合 rescue error_type => var
の形式を使えば
例外オブジェクトを得られます。このオブジェクトは組み込み
変数 $! でも得られます。また例外が
発生したソースコード上の位置は変数 $@ に格納されます。
raise は Ruby の予約語ではなく、Kernel モジュールで 定義されている関数的メソッドです。
例:
begin do_something rescue recover ensure must_to_do end
文法:
begin 式.. [rescue [error_type,..] [=> evar] [then] 式..].. [else 式..] [ensure 式..] end
本体の実行中に例外が発生した場合、rescue
節(複数指定できます)が
与えられていれば例外を捕捉できます。発生した例外と一致する
rescue
節が存在する時には rescue
節の本体が実行されます。
発生した例外は $! を使って参照することができます。また、
指定されていれば変数 evar にも $!
と同様に発生した例外が格
納されます。
begin raise "error message" rescue => evar p $! p evar end # => #<RuntimeError: error message> #<RuntimeError: error message>
例外の一致判定は例外のクラスが rescue
節で指定したクラスと同じか
またはサブクラスであるかどうか Object#kind_of? を用いて判
定されます*5。
error_type が省略された時は StandardError のサブクラスであ る全ての例外を捕捉します。Rubyの組み込み例外は(SystemExit や Interrupt のような脱出を目的としたものを除いて) StandardError のサブクラスです。
例外クラスのクラス階層については 例外クラス を参照してください。
rescue
では error_type は通常の引数と同じように評価され、
そのいずれかが一致すれば本体が実行されます。error_type を評価し
た値がクラスやモジュールでない場合には例外 TypeError が発生しま
す。
省略可能な else
節は、本体の実行によって例外が発生しなかった場合
に評価されます。
ensure
節が存在する時は begin
式を終了する直前に必ず
ensure
節の本体を評価します。
begin
式が返す値は ensure
が実行される直前に評価された式で
す。
例:
open("nonexistent file") rescue STDERR.puts "Warning: #$!"
文法:
式1 rescue 式2
式1で例外が発生したとき、式2を評価します。 以下と同じ意味です。捕捉する例外クラスを指定することはできません。 (つまり、StandardError 例外クラスのサブクラスだけしか捕捉できません)
begin 式1 rescue 式2 end
rescue修飾子を伴う式の値は例外が発生しなければ式1、例外が発生すれば式2 です。ただし、大抵の場合、優先順位の都合により式全体を括弧で囲む必要が あります。
var = open("nonexistent file") rescue false p var => nil # 値を持たない変数 var が定義されただけ var = (open("nonexistent file") rescue false) p var => false
特にメソッドの引数に渡す場合は二重に括弧が必要となります。
p(open("nonexistent file") rescue false) => parse error p((open("nonexistent file") rescue false)) => false
例:
return return 12 return 1,2,3
文法:
return [式[`,' 式 ... ]]
式の値を戻り値としてメソッドの実行を終了します。式が2つ以上
与えられた時には、それらを要素とする配列をメソッドの戻り値と
します。式が一つもない場合には nil
が戻り値とな
ります。
例:
BEGIN { ... }
文法:
BEGIN '{' 文.. '}'
初期化ルーチンを登録します。BEGIN
直後の
{ }
内部(BEGIN
ブロック)で指定した文
は当該ファイルのどの文が実行されるより前に実行されます。複数
のBEGIN
が指定された場合には指定された順に実行さ
れます。
BEGIN
ブロックは独立したローカル変数のスコープを
導入するため、ローカル変数を外部と共有する事はありません。情
報の伝達にはグローバル変数を使う必要があります。
BEGIN
はトップレベルにしか置く事はできません。
例:
END { ... }
文法:
END '{' 文.. '}'
「後始末」ルーチンを登録します。END
ブロックで指
定した文はインタプリタが終了する時に実行されます。
複数の END
ブロックを登録した場合、その実行順序は
登録とは逆順です。
END
ブロックは、BEGIN
ブロックとは異
なり、イテレータと同様のスコープを持ち、周囲とスコープを共有
します。
END
ブロックは最初の1回だけしか評価
されないので以下のようにループの中で実行しても複数のEND
ブロックを登録できません。同一の後始末処理を複数登録する必要がある場合には
at_exitを使ってください。
5.times {|i| END { p i } } # => 0
END
もトップレベルにしか置く事はできません*6。
それと、END
で登録された実行文を取り除く事はで
きません。
END
ブロックの中で発生した例外はそのEND
ブロックを中断しますが、すべての後始末ルーチンが実
行されるようインタプリタを終了させずにメッセージだ
けが出力されます。
例:
END { p "FOO" } END { raise "bar"; p "BAR" } END { raise "baz"; p "BAZ" } => baz (RuntimeError) bar (RuntimeError) "FOO"
*1にもかかわらずその値の説明が無い
*2あらい 2002-01-15: version 1.6 の場合、return, break, next, redo,
retry, while, until, class, module, def は値を返しません。BEGIN, END
もかな?値を返さない式を代入式の右辺に置くと parse error になります。
また、メソッド定義文の最後が値を返さない式である場合(return で抜けない
場合)、そのメソッドの戻り値は nil
です。このページに載っているも
ので値を返すものは if, if修飾式, unless, unless 修飾式, case, begin,
rescue 修飾式 です。これらは書いておきました。また、break, next による
戻り値の影響も書いておきました。version 1.7 ではまた違います。
ruby 1.7 feature参照
*3あるいは、突然エラーになってインタプリタが終了します。
retry #=> -:1: retry outside of rescue clause
*4あらい 2002-01-13: ensure は大域脱出を捕まえるので retry が (d)
に飛んでいるあまり良い例じゃないか
*5ruby 1.7 feature: 1.7 での例外の一致判定は
Module#=== を用いて行われます
*6あらい 2001-10-06 本当?
あらい 2002-01-15: どうやら、(1) eval() による評価で (2) def の中で END す
るとparse error のようです。よくわからない条件です