工藤探偵事務所

Resarch and Investigation

ルビー3分クッキング 第十回「反復からの卒業」の巻

kudo-shunsaku2008-04-17


3分間で覚える Ruby のトピックスです。
でも、本家同様に実際には10分程度掛かります。

大事なのは小さな積み重ねと継続する才能ですね。
一番の怠け者の俺が言うのも変ですけどね。


第十回は、「反復からの卒業」の巻です。

今回は「制御構造」ということで、また「繰り返し」をやりましょう。
それと、ここで繰り返し処理から抜け出す幾つかの方法がありますので、それをお話したいと想います。

まずは、繰り返しの基本から。
ファイル名は、"break_next.rb" などとして下さい。

#! ruby -Ks

# 繰り返し炒めます。
i = 0
while i < 5
  puts "葫を炒めます。"
  i += 1
end

puts

# 決まった回数を繰り返すときには。
5.times {
    puts "人参を炒めます。"
}

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby break_next.rb
葫を炒めます。
葫を炒めます。
葫を炒めます。
葫を炒めます。
葫を炒めます。

人参を炒めます。
人参を炒めます。
人参を炒めます。
人参を炒めます。
人参を炒めます。

C:\rubycode\3minutes2cooking>

繰り返しでつかう制御文では "while" が使えます。条件式が真の間繰り返し行うことが出来ます。

また繰り返しの回数が決まっている場合には、便利な記述が出来ます。数字(Integerクラス)には、"times" という繰り返しのメソッドがありこれが有効です。ちょっと不思議な書き方ですけど、これも Ruby ぽさの様子です。ここでは、"5.times" としているので5回繰り返してくれます。

先ほどの "while" は条件が充たしている間だけ繰り返しますが、ただ繰り返すだけものもあります。組み込み関数の "loop" メソッドです。

先ほどのソースコードに追加します。

# 永遠に繰り返すときには。
loop {
    puts "玉葱を炒めます。"
}

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby break_next.rb

    :
    :
玉葱を炒めます。
玉葱を炒めます。
玉葱を炒めます。
玉葱を炒めます。
玉葱を炒めます。
break_next.rb:54: Interrupt
        from break_next.rb:16:in `loop'
        from break_next.rb:16

C:\rubycode\3minutes2cooking>

実行すると永遠に標準出力への印字を繰り返します。コマンドプロンプトの画面で "CTRL+C" (強制終了)と押して中断して下さいませ。無理矢理に終らせることが出来ます。

永遠に繰り返したい場合に便利ですね。
永遠に「何を繰り返すのか?」が問題ですけどね(笑)

では次にこの繰り返しなどから脱出する方法を幾つかご紹介します。
先ほどの "loop" などでの無限ループのあるプログラムを実行するといつまでも終了しませんよね。この他にも様々な制御構造から脱出する際の方法としても使えます。

先ほどのソースコードを変更します。

# 狐色フラグ。フラグが真になったら狐色の筈です。
fox_flag = nil

# 炒め時間の設定。秒数です。
sauter_second = 10

# 炒め始める時間です。
start_time = Time.now 

# 充分に炒めたら繰り返しを終わります。
loop {
    unless fox_flag
        puts "玉葱を炒めます。"
        sleep(2)
    else
        puts "玉葱が狐色になりました。"
        break
    end
    
    # 現在時間。
    porgress_time = Time.now
    
    # 経過時間の判定。
    if sauter_second < (porgress_time - start_time)
        fox_flag = "brown"
    end
}

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby break_next.rb

    :
    :
玉葱を炒めます。
玉葱を炒めます。
玉葱を炒めます。
玉葱を炒めます。
玉葱を炒めます。
玉葱を炒めます。
玉葱が狐色になりました。

C:\rubycode\3minutes2cooking>

先ほどの永遠に炒める "loop" の処理を変えました。

ここでは、あらかじめ炒める時間を決めて、その時間が経過したら終了するようにしてあります。ちょっとクドイ書き方かもしれませんが、見てなるべく判り易いようにしました。

繰り返しを脱出するために使っているのは、"break" です。"break" では処理をそこで中断し繰り返しを脱出してくれます。

またここでは時間を扱うために "Time.now" としていますが、これも組み込み関数で現在の時間が取得出来ます。"Time" クラスの中身は秒数です。炒めているうちに時間が経過するので、毎回それをチェックして指定した秒数が経過しましたら、自動的に終了します。

先ほど繰り返しを脱出するために使ったのは、"break" です。大体、"break" でことは足りる場合が多いかもしれませんが、その仲間には "next", "redo" といったキーワードもあります。

今度は配列での繰り返しで違いを試してみましょう。

先ほどのソースコードに追加します。

puts

# カルボナーラ食材の配列です。
carbonara = ["garlic", "bacon", "butter", "cream", "yolk", "cheese"]

#idx = 0
turn_off = 2
carbonara.each_with_index { |ingredient, idx|
    if idx == turn_off
        break
    end
    
    puts ingredient
}

puts

carbonara.each_with_index { |ingredient, idx|
    if idx == turn_off
        next
    end
    
    puts ingredient
}

puts

carbonara.each_with_index { |ingredient, idx|
    if idx == turn_off
        redo
    end
    
    puts ingredient
}

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby break_next.rb

    :
    :

garlic
bacon

garlic
bacon
cream
yolk
cheese

garlic
bacon
break_next.rb:145: Interrupt
        from break_next.rb:155:in `each_with_index'
        from break_next.rb:144:in `each'
        from break_next.rb:144:in `each_with_index'
        from break_next.rb:144

C:\rubycode\3minutes2cooking>

"each_with_index" メソッドによる同じ繰り返しで、3つの脱出方法を試みました。
"break" は先ほど説明した通りすぐさま脱出しますが、次の "next" ではその部分だけをスキップして「次の」処理を実行しています。"butter" だけ出力されていないことに気が付きましたか?

そして "redo" ですが、その名の通りスキップせずに同じ箇所を繰り返し実行します。なのでサンプルでは何時まで経っても条件が成立しないので、無限ループに陥ってます。先ほどと同じく、コンソール画面から "Ctrl-C" (中断のシグナル)と押してプログラムを終了させて下さい。


折角、 "redo" を使っているので無限ループにならないように書き換えてみましょう。

先ほどのソースコードを書き換えます。

idx = 0
turn_off = 2
carbonara.each { |ingredient|
    idx += 1
    if idx == turn_off
        redo
    end
    
    puts ingredient
}

実行時、コンソール画面。

C:\rubycode\3minutes2cooking>ruby break_next.rb

    :
    :
garlic
bacon
butter
cream
yolk
cheese

C:\rubycode\3minutes2cooking>

今度はちゃんと終わりました。先ほどは "each_with_index" メソッドを使ったので配列の添え字と値が連動していましたが、今度は単に "each" メソッドを使い添え字とは無関係に繰り返しを行ったからです。結果、カルボナーラの食材、全部を印字してくれました。

この使い方が "redo" の本領発揮という訳ではないですが、「もう一度繰り返して実行してくれる」というところが味噌だということが判りました。例えば、ファイルのオープンの時とかになど一度失敗してもその後同じことをやってみてくれ、といった時などに使えそうです。

今回は繰り返し制御文である "while" と "loop"。それと繰り返しから脱出するための "break", "next", "redo" といったキーワードをご紹介しました。これら制御構造を使いこなせば、思い通りのシナリオでプログラムを書けることに近づくのではないかと考えます。「コントロールは支配である」とも言えますしね。




「たら〜こ〜♪たっぷり♪たっぷり♪た〜らこ〜♪」

今日はここまで。次回をお楽しみに。