Top > Ruby > 条件分岐・繰り返し

このページの概要

  • RubyにはBASIC時代の悪名高い(?)gotoがありませんが、条件分岐と繰り返しを理解することによって、プログラミングを順次実行だけではない次のステップへと移行できます。
  • 繰り返しは説明の便宜上 printf を使っています。printf("数字ですよ「%d」",x) で 数字ですよ「1」 みたいに x の中身を数字で表示せよ、という形式で使っているため、そんなに難しくはないと思います。書式の詳細はRubyリファレンスマニュアルのsprintfフォーマットをどうぞ。
  • 動作確認環境 : Ruby 1.8.4

基本は上から下へ。

下のコードを実行すると、
2[改行]3[改行]4[改行]5[改行]6[改行]
と表示されます。つまり、上(2行目)から下まで順番に実行されて、最後の行(6行目)で処理が終わったということを意味します。

  0
  1
  2
  3
  4
  5
#!/usr/bin/env ruby
puts __LINE__       #=> 2
puts __LINE__       #=> 3
puts __LINE__       #=> 4
puts __LINE__       #=> 5
puts __LINE__       #=> 6

もし○○なら (if分岐)

もし○○なら××しなさい、という処理は、if(条件式) thenという文で行います。
次の行から end がくるまでの間は、条件式を満たす場合は実行され、条件式を満たさない場合はスキップされます。(if 以外にも、その行からendまでの間××する、という書き方はかなりあります。区別をしやすいように、そのような部分は半角スペース2文字の字下げを行ってください。)

then は if(条件式)で改行を入れる場合は省略できますが、入れた方がいいでしょう。

下記コードの場合、1以上の数字をキー入力したら 4 6 7 8 が表示されますが、0以下や文字を入れた場合だと 6 7 8 だけとなり、4行目がスキップされたことがわかります。

  0
  1
  2
  3
  4
  5
  6
  7
#!/usr/bin/env ruby
input = gets.to_i
if(input >= 1) then
  puts __LINE__     #=> 4
end
puts __LINE__       #=> 6
puts __LINE__       #=> 7
puts __LINE__       #=> 8

1~だけではなく、1~10 の間の処理をしたい場合は、2通りの書き方があります。

  • if の中に if を書く方法 (「もし 1~ならば」、の中に「もし~10ならば」を入れる)
      0
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
    
    #!/usr/bin/env ruby
    input = gets.to_i
    puts __LINE__       #=> 3
    if(input >= 1) then
      if(input <= 10) then
        puts __LINE__   #=> 6 (1~10以外だとスキップされる)
      end
    end
    puts __LINE__       #=> 9
    puts __LINE__       #=> 10
    puts __LINE__       #=> 11
    
  • ひとつの if の中に &&(AND式) や ||(OR式) を書く方法 (「もし 1~ならば」かつ(AND)「もし~10ならば」とする)
      0
      1
      2
      3
      4
      5
      6
      7
      8
    
    #!/usr/bin/env ruby
    input = gets.to_i
    puts __LINE__       #=> 3
    if((input >= 1) && (input <= 10)) then
      puts __LINE__     #=> 5 (1~10以外だとスキップされる)
    end
    puts __LINE__       #=> 7
    puts __LINE__       #=> 8
    puts __LINE__       #=> 9
    

もし○○なら、そしてもし△△なら、そしていずれも違ったら (if then elsif then else分岐)

入力された値が1~10の場合、11~20の場合、それ以外の場合で処理を分けたいと思った場合は、if と end の間に elsif then と else を配置して条件分岐できます。then は改行があれば省略可能です。elsif(他の条件式) や else(if と elsif の条件をすべて満たさない場合) も分岐処理不要なら省略できます。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
#!/usr/bin/env ruby
input = gets.to_i
puts __LINE__       #=> 3
if((input >= 1) && (input <= 10)) then
  puts __LINE__     #=> 5 (1~10の処理)
elsif((input >= 11) && (input <= 20)) then
  puts __LINE__     #=> 7 (11~20の処理)
else                #else then という書き方はエラーになります。
  puts __LINE__     #=> 9 (それ以外の処理)
end
puts __LINE__       #=> 11
puts __LINE__       #=> 12
puts __LINE__       #=> 13

もし○○じゃなかったら、そして○○だったら (unless then else分岐)

if(!条件式) then は、 unless(条件式) then に置き換えることもできます。ただし、elsif が使えません。つまり、最大でも「○○と違うか、○○か」の二者択一になります。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
#!/usr/bin/env ruby
input = gets.to_i
puts __LINE__       #=> 3
unless((input >= 1) && (input <= 10)) then
  puts __LINE__     #=> 5 (1~10以外の処理)
else
  puts __LINE__     #=> 7 (それ以外の処理、つまり1~10の処理)
end
puts __LINE__       #=> 9
puts __LINE__       #=> 10
puts __LINE__       #=> 11

このケースだったら○○、あのケースだったら△△、該当しなかったら
(case when when else end 分岐)

1~10のときは○○、11~20のときは△△、21~30のときは××...という処理を if 分岐で書くのは大変ですが、Rubyだと case when という文を使ってすっきり書くことができます。when 部分には 1..10 といった書式が使えるので、かなり可読性に貢献すると考えられます。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
#!/usr/bin/env ruby
input = gets.to_i
puts __LINE__       #=> 3
 
case input
when 1..10
  puts __LINE__     #=> 7 (1~10の処理)
when 11..20
  puts __LINE__     #=> 9 (11~20の処理)
else
  puts __LINE__     #=> 11 (それ以外の処理)
end
 
puts __LINE__       #=> 14
puts __LINE__       #=> 15
puts __LINE__       #=> 16

指定回数繰り返す (forによる繰り返し)

x 回繰り返ししたい処理がある場合は、for i in 1..x という記述で可能です。

  0
  1
  2
  3
  4
  5
  6
  7
  8
#!/usr/bin/env ruby
 
printf("Line:%d\n",__LINE__)               #=>Line:3
 
for i in 1..10
  printf("Line:%d, Count:%d\n",__LINE__,i) #=>Line:6, Count:○○ (○○= 1 ~ 10)
end
 
printf("Line:%d\n",__LINE__)               #=>Line:9

1..x (1~x で x 回繰り返し) だけではなく、[x, y, z] の配列形式の書式(または配列型の変数)を渡すことにより、1 2 3 5 7 11 みたいな変則的な実行も可能です。

  0
  1
  2
  3
  4
  5
  6
  7
  8
#!/usr/bin/env ruby
 
printf("Line:%d\n",__LINE__)               #=>Line:3
 
for i in [1, 2, 3, 5, 7, 11]
  printf("Line:%d, Count:%d\n",__LINE__,i) #=>Line:6, Count:○○ (○○= [1, 2, ○○] の部分で指定した配列だけが入る)
end
 
printf("Line:%d\n",__LINE__)               #=>Line:9

特定条件を見て繰り返す (whileによる繰り返し、untilによる繰り返し)

 特定の条件を満たし続ける限り、繰り返しし続けるという構文 while があります。一方で、特定の条件を*満たさない限り*繰り返しし続ける構文 until もあります。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
#!/usr/bin/env ruby
 
printf("Line:%d\n",__LINE__)               #=>Line:3
 
i = 0
while (i<=5)
  printf("Line:%d, Count:%d\n",__LINE__,i) #=>Line:7, Count:○○ ( 0 1 2 3 4 5)
  i = i + 1 #←この行を忘れると、いつまでも i = 0 になってしまい無限ループとなります。
end
 
printf("Line:%d\n",__LINE__)               #=>Line:11
  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
#!/usr/bin/env ruby
 
printf("Line:%d\n",__LINE__)               #=>Line:3
 
i = 0
until (i>5)                                # i<=5 の真逆の条件ですが、until が while の真逆なので、上記の while と同じ意味になります。
  printf("Line:%d, Count:%d\n",__LINE__,i) #=>Line:7, Count:○○ ( 0 1 2 3 4 5)
  i = i + 1
end
 
printf("Line:%d\n",__LINE__)               #=>Line:11

上のコードでは、わかりやすいように数字の大小を繰り返し条件としていますが、実際にはキー入力やファイル処理・ネットワーク通信の「終了を検知するまで」繰り返させる、というのが一般的です。

イテレータ (配列名.each do |取り出し変数| ... end または 配列名.each {|取り出し変数| ... })

for i in 1..10 の応用みたいな方法として、イテレータというものがあります。 範囲を示す変数(Range、1..10がいい例)や配列型(Array, ["a","bb","ccc"] が例)を筆頭に、each で変数をひとつずつ取り出して、範囲や配列が終わるまでループさせる、というのがその役割です。

下のサンプルは2通りの書き方をしていますが、いずれも myloop という「範囲を示す変数」を最初から最後まで、1個ずつ value に取り出しながら繰り返ししています。個人的には { } を使っていない前者の方が Ruby らしいと思います。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
#!/usr/bin/env ruby
myloop = 1..10
 
#イテレータの例 その1
myloop.each do |value|
  puts value
end
 
#イテレータの例 その2
myloop.each {|value|
  puts value
}

イテレータ (times や upto を使うケース)

 「myloop = 1..10」といった定義をしなくても繰り返しする方法があります。10回繰り返すコードを2通り書いてみましょう。

  0
  1
  2
  3
  4
  5
  6
  7
  8
  9
#!/usr/bin/env ruby
#イテレータの例 その3
10.times do |value|
  puts value       #=>0 1 2 ... 7 8 9
end
 
#イテレータの例 その4
1.upto(10) do |value|
  puts value       #=>1 2 3 ... 8 9 10
end

中断されない限り無限ループ (loop)

 構文 loop { }を使うと、break exit 等のループ脱出のキーワードを使うか、CTRL+C 等でスクリプトを強制中断しない限り無限ループとなります。なお、この loop はイテレータの一種のようで、loop = 範囲型/配列型の値 をいれてやると、無限ループではなくなってしまいます(loop の役割が上書きされてしまう)。loop 変数の取り扱いにはいろいろな意味で注意しましょう。

  0
  1
  2
  3
#!/usr/bin/env ruby
loop {
  puts __LINE__  #=> 3 (が何らかの理由でスクリプト停止させられるまで延々と表示され続ける)
}

繰り返しの流れを変更する
(end 以降へジャンプする break、条件式直前へジャンプする next、条件式直後へジャンプする redo、評価やりなおしの retry)

 for while loop といった繰り返し文の途中で処理の流れを換えることができます。  break next redo retry がそのキーワードです。これらのキーワードは「最も内側の繰り返し」に作用してコードの実行順序を変更することができます。

  • 繰り返し終了 break のサンプル
      0
      1
      2
      3
      4
      5
      6
      7
      8
    
    #!/usr/bin/env ruby
    #=>1 2 3 4 5 6 (7 表示直前で繰り返し終了となり、終了)
    for i in 1..10
      if (i == 7) then
        break #ジャンプ元
      end
      puts i
    end
    #break のジャンプ先
    
  • 繰り返しを次ステップへ進める next のサンプル
      0
      1
      2
      3
      4
      5
      6
      7
    
    #!/usr/bin/env ruby
    #=>1 2 3 4 5 6 8 9 10 (7 のときだけ前方にジャンプされてしまい、putsが実行されません)
    for i in 1..10 # next のジャンプ先 (i in 1..10 のあたり)
      if (i == 7) then
        next #ジャンプ元
      end
      puts i
    end
  • 条件式を確認せずに繰り返しをやり直す redo のサンプル
      0
      1
      2
      3
      4
      5
      6
      7
      8
      9
    
    #!/usr/bin/env ruby
    #=>「 _1」「 _2」「 _3」「 _4」「 _5」「 _6」のあとは延々と _ だけが表示(無限ループ化している)
    for i in 1..10 
      # redo のジャンプ先 (条件式の次行。よって、for の評価・加算や while の評価は行われない)
      printf(" _")
      if (i == 7) then
        redo #ジャンプ元
      end
      puts i 
    end
  • 繰り返しを「再起動」してしまう retry のサンプル
      0
      1
      2
      3
      4
      5
      6
      7
    
    #!/usr/bin/env ruby
    #=>1 2 3 4 5 6 1 2 3 4 5 6 ...(無限ループ)
    for i in 1..10 # retry のジャンプ先 (in 1..10 のカウンタが 1 にリセットされる!)
      if (i == 7) then
        retry #ジャンプ元
      end
      puts i
    end

break next redo retry の各キーワードは、if (条件) then break; end を break if (条件) のように記述することもできます。


リロード   新規 下位ページ作成 編集 凍結 差分 添付 コピー 名前変更   ホーム 一覧 検索 最終更新 バックアップ リンク元   ヘルプ   最終更新のRSS
Last-modified: Fri, 22 Jul 2011 21:57:59 JST (1525d)