[TOP][UP][<-PREV][NEXT->]

制御構造

Rubyでは(Cなどとは異なり)制御構造は式であって、何らかの値を 持ちます。 *1 *2 RubyはC言語やPerlから引き継いだ制御構造を持ちますが、 その他にイテレータというループ抽象化の機 能があります。イテレータは繰り返しを始めとする制御構造をユー ザが定義する事が出来るものです.

条件分岐

if

例:

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 の条件式が正規表現のリテラルである時には

$_ =~ リテラル

であるかのように評価されます。

if 修飾子

例:

print "debug\n" if $DEBUG

文法:

式 if 式

右辺の条件が成立する時に、左辺の式を評価してその結果を返します。 条件が成立しなければ nil を返します。

unless

例:

unless baby?
  feed_meat
else
  feed_milk
end

文法:

unless 式 [then]
  式 ...
[else
  式 ... ]
end

unless は条件実行を行いますが、条件が if と反対で、条件が偽の時に実行を行います。unless 式に elsif を指定することはできません。

unless 修飾子

例:

print "stop\n" unless valid(passwd)

文法:

式 unless 式

右辺の条件が成立しない時に、左辺の式を評価してその結果を返します。 条件が成立しなければ nil を返します。

case

例:

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

例:

while sunshine
  work()
end

文法:

while 式 [do]
   ...
end

式を評価した値が真の間、本体を繰り返して実行します。 while は、値を返しません。

while 修飾子

例:

sleep while idle

文法:

式 while 式

右辺の式を評価した値が真の間、左辺を繰り返して実行します。

左辺の式が begin である場合には、それを最低一度は 評価します。

例:

begin
  sleep
end while idle

while 修飾した式は値を返しません。

until

例:

until sunrise
  sleep
end

文法:

until 式 [do]
   ...
end

式を評価した値が真になるまで、本体を繰り返して実行します。 until は、値を返しません。

until修飾子

例:

work until tired

文法:

式 until 式

右辺の式を評価した値が真になるまで、左辺を繰り返して実行しま す。

左辺の式が begin である場合には、左辺を最低一度は 評価します。

例:

begin
  sleep
end until sunrise

until 修飾した式は値を返しません。

for

例:

for i in [1, 2, 3]
  print i*2, "\n"
end

文法:

for lhs ...  in 式 [do]
  式..
end

式を評価した結果のオブジェクトの各要素に対して本体を繰り返し て実行します。これは以下の式とほぼ等価です。

(式).each `{' `|' lhs..`|' 式.. `}'

「ほぼ」というのは、do ... endまたは{ }による ブロックは新しいローカル変数の有効範囲を導入するのに対し、 for文はローカル変数のスコープに影響を及ぼさない点が 異なるからです。

for は、in に指定したオブジェクトの each メソッドの戻り値を返します。

break

例:

i=0
while i<3
  print i, "\n"
  break
end

文法:

break

break はもっとも内側のループを脱出します。ルー プとは

のいずれかを指します。Cと違い、breakはループを 脱出する作用だけを持ち、case を抜ける作用は持ち ません。

break によりループを抜けた、for やイテレータは、nil を返します。

next

例:

next

文法:

next

nextはもっとも内側のループの次の繰り返しにジャンプします。 イテレータでは、yield 呼出し の脱出になります。

next により抜けた yield 式は nil を返します。

redo

例:

redo

文法:

redo

ループ条件のチェックを行なわず、現在の繰り返しをやり直します。

retry

例:

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)

*4

例外処理

raise

例:

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

例:

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の組み込み例外は(SystemExitInterrupt のような脱出を目的としたものを除いて) StandardError のサブクラスです。

例外クラスのクラス階層については 例外クラス を参照してください。

rescue では error_type は通常の引数と同じように評価され、 そのいずれかが一致すれば本体が実行されます。error_type を評価し た値がクラスやモジュールでない場合には例外 TypeError が発生しま す。

省略可能な else 節は、本体の実行によって例外が発生しなかった場合 に評価されます。

ensure 節が存在する時は begin 式を終了する直前に必ず ensure 節の本体を評価します。

begin式が返す値は ensure が実行される直前に評価された式で す。

rescue修飾子

例:

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
return 12
return 1,2,3

文法:

return [式[`,' 式 ... ]]

式の値を戻り値としてメソッドの実行を終了します。式が2つ以上 与えられた時には、それらを要素とする配列をメソッドの戻り値と します。式が一つもない場合には nil が戻り値とな ります。

BEGIN

例:

BEGIN {
   ... 
}

文法:

BEGIN '{' 文.. '}'

初期化ルーチンを登録します。BEGIN直後の { }内部(BEGINブロック)で指定した文 は当該ファイルのどの文が実行されるより前に実行されます。複数 のBEGINが指定された場合には指定された順に実行さ れます。

BEGINブロックは独立したローカル変数のスコープを 導入するため、ローカル変数を外部と共有する事はありません。情 報の伝達にはグローバル変数を使う必要があります。

BEGINはトップレベルにしか置く事はできません。

END

例:

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 のようです。よくわからない条件です


[TOP][UP][<-PREV][NEXT->]