tanish-kr's learning log

Learning output log

Ruby

制御構造

条件分岐

if

条件式を評価した結果が真である場合、then以下の式を評価します。ifの条件式が偽であれば、elsifの条件を評価します。すべてのifおよびelseifの条件式が偽であった場合、elseが評価されます。else節がなくいずれの条件も成り立たなければnilを返します。Rubyではfalseまたはnilだけが偽で、それ以外は0や空文字も含めすべて真です。

if n.zero?
  puts "0です"
elsif n.even?
  puts "偶数です"
elsif n.odd?
  puts "奇数です"
end

if修飾子

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

e.errors.messages if e.valid?

unless

unlessはifと反対で、条件式が偽のときにthen以下の式を評価します。unless式にelsifを指定することはできません。

unless n.zero?
  puts "0ではありませんでした"
else
  puts "0です"
end

unless修飾子

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

puts "0ではありませんでした" unless n.zero?

case

caseは一つの式に対する一致判定による分岐を行います。when節で指定された値と最初の式を評価した結果とを演算子===を用いて比較して、一致する場合はwhen節の本体を評価します。

case age
when 0..2
  "baby"
when 3..6
  "little child"
when 13..18
  "youth"
else
  "adult"
end
  • ===メソッドのオブジェクトごとの動作
メソッドオブジェクト 動作
Range 引数が自身の範囲内に含まれるなら真を返す
Regexp 引数の文字列がマッチした場合、真を返す
Proc 右辺を引数にして手続きオブジェクトを実行した結果を返す
Module, Class 引数が自身または自身のサブクラスのインスタンスであれば真を返す

繰り返し

while

式を評価した値が真の間、本体を繰り返し実行します。breakによりwhile式の戻り地をその値にすることもできます。

ary = [0, 4, 4, 8, 16]
i = 0
while i < ary.length
  print ary[i]
  i += 1
end

while 修飾子

右辺の式を評価した値が真の間、左辺を繰り返し実行します。
左辺の式がbegin節である場合にはそれを最初に1回評価してから繰り返します。

send_request(data)
begin
  res = get_response()
end while res == "Continue"

until

式を評価した値が真になるまで、本体を繰り返して実行します。 引数を伴ったbreakによりuntil式の戻り値をその値にすることもできます。

until修飾子

右辺の式を評価した値が真になるまで、左辺を繰り返して実行します。
左辺の式がbegin節である場合にはそれを最初に1回評価してから繰り返します。

send_request(data)
begin
  res = get_response()
end until res === "OK"

for

式を評価した結果のオブジェクトの各要素に対して本体を繰り返して実行します。

for i in 5.times
  p i
end

break

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

以下のいずれかを指します

  • while
  • untile
  • for
  • イテレータ
i = 0
while i < 3
  print i, "\n"
  break
end

next

nextはもっとも内側のループの次の繰り返しにジャンプします。イテレータでは、yield呼び出しの脱出になります。nextにより抜けたyield式はnilを返します。ただし、引数を指定した場合、yield式の戻り値はその引数になります。

ARGF.each do |line|
  next if line.strip.empty?
  print line
end

retry

retryは、rescue節でbegin式をはじめからもう一度実行するのに使用します。retryを使うことである処理が成功するまで処理を繰り返すようなループを作ることが出来ます。
rescue節以外でretryが用いられた場合には例外、SyntaxErrorが発生します。

begin
  do_something
rescue StandardError => e
  retry
end

例外処理

例外とは何らかの異常が発生したことを表すオブジェクトです。

例外の発生と例外クラス

プログラムの実行中にエラーが有ると例外が発生します。ある場所で例外が発生したとき、例外はコールスタックをさかのぼり、最終的にはトップレベルまで伝搬していきます。

例外クラス

  • Exception : すべての例外クラスの祖先クラス
    • NoMemoryError : メモリの確保に失敗すると発生
    • ScriptError : スクリプトのエラーを表す外クラス
      • LoadError : Kernel.#requireKernel.#loadが失敗した時に発生
      • NotImplementedError : 現在のプラットフォームで実装されていない機能が呼び出された時に発生
      • SyntaxError : ソースコードに文法エラーがあったときに発生
    • SecurityError : セキュリティ上の問題が起きた時に発生
    • SignalException : 補足していないシグナルを受け取った時に発生
      • Interrput : SIGINTシグナルを補足していない時にSIGINTシグナルを受け取ると発生
    • StandardError : 通常のプログラムで発生する可能性が高い例外クラスを束ねるためのクラス。独自で例外クラスを作成する場合は、基本このクラスを継承することが推奨されている
      • ArgumentError : 引数の数があっていないときや、数は合っていて、期待される振る舞いを持ってはいるが、期待される値では無い時に発生
        • UncaughtThrowError : Kernel.#throwに指定したtagに対して一致するKernel.#catchが存在しない場合に発生
      • EncodingError : エンコーディング関連の例外の基底クラス
      • FiberError : Fiberに関するエラーが起きると発生
      • IOError : 入出力でエラーが起きると発生
        • EOFError : EOFに達成した時に発生
      • IndexError : 添字が範囲外の場合に発生
        • KeyError : Hash#fetchなどでkeyに対応するvalueがない場合に発生
        • StopIteration : イテレーションを止める時に発生
          • ClosedQueueError : close済みのThread::QueueThread::SizedQueueに許可されていない操作を行おうとした場合に発生
      • LocalJumpError : あるProcオブジェクトの作成元スコープがすでに終了しているとき、そのProcオブジェクト内でreturn, break, retryのいずれかを実行すると発生
      • Math::DomainError : 数学関数(Mathのモジュール関数)で与えた引数が定義域に含まれていな場合に発生
      • NameError : 未定義のローカル変数や定数を使用した時に発生
        • NoMethodError : 定義されていないメソッドの呼び出しが行われた時に発生
      • RangeError : 範囲に関する例外クラス。値が定義域から外れている時に発生
        • FloatDomainError : 正負の無限大やNaN(Not a Number)をBignumに変換しようとしたり、NaNとの比較を行った時に発生
      • RegexpError : 正規表現のコンパイルに失敗した時に発生
      • RuntimeError : 特定の例外クラスに該当しないエラーが起こった時に発生。Kernel.#raiseで例外クラスを指定しなかった場合もRuntimeErrorが発生する
        • FrozenError : Object#freezeされたオブジェクトを変更しようとした時に発生
      • SystemCallError : Rubyの実装に用いられているシステムコールまたは一部のC言語関数が失敗した時に発生
      • ThreadError : Thread関連のエラーが起きたときに発生
      • TypeError : メソッドの引数に期待される型ではないオブジェクトや、期待される振る舞いを持たないオブジェクトが渡された時に発生
      • ZeroDivisionError : 整数に対し整数の0で除算を行ったときに発生
    • SystemExit : Rubyインタプリタを終了させるときに発生
    • SystemStackError : システムスタックがあふれた時に発生

raise

例外を発生させます。raiseの第一引数に文字列を指定した場合は、それを例外のメッセージとしてRuntimeErrorを発生させます。

raise "Error #{e}"
raise StandardError, "error"

begin

本体の実行中に例外が発生した場合、rescue節が与えられていれば、例外を補足できます。発生した例外と一致するrescue節が存在する場合は、rescue節の本体が実行されます。変数を指定すると発生した例外が格納されます

begin
  raise "error"
rescue StandardError => e
  p e
  p $!
end
  • 例外クラスを複数指定
begin
  do_process
rescue ArgumentError, NameError => e
  puts e
end
  • 発生した例外クラスによってrescueを分ける
begin
  do_process
rescue ArgumentError => e
  puts e
rescue NameError => e
  raise e
end

ensure

例外の有無に関わらず、最後にかならず実行したい処理はensure節に記述します。

begin
  file = File.open("input.txt")
  do_process file
rescue IOError => e
  puts e
ensure
  file.close if file
end

elseを使うと、例外が発生しなかった場合だけ行う処理を記述することもできます。

begin
  file = File.open("input.txt")
  do_process file
rescue IOError => e
  puts e
else
  puts "success file process"
ensure
  file.close if file
end

例外処理の戻り値

begin節、rescue節、またはelse節で最後に評価された式の値が戻り値になります

result = begin
          "success"
         rescue StandardError => e
          puts e
          "failed"
         ensure
          puts "this is not return value" # 実行されるが戻り値とはならない
         end

メソッドやクラス/モジュール定義で例外を補足する

resucue節、ensure節、else節はbegin以外にも組み合わせて使用することが出来ます。これらはメソッド定義式、クラス定義式、モジュール定義式に記述することが出来ます。

# メソッド呼び出し時に発生した例外を補足する
def do_process
  "hoge"
rescue StandardError => e
  puts e
end
# クラス定義時に発生したエラーを補足する
class Klass
  def initialize
    super
  end
rescue StandardError => e
  puts e
end