情報科学概論
2003.6.24

Back to index page



  1. 本日の作業内容

  2. 制御構造 (繰り返し2)

    • while ループの基礎

      for 文のように一定条件の増分で繰り返しを行うのではなく,より自 由な条件判断で繰り返しを行う方法がこの while になります. 英語の意味から分かるように「〜の間」という意味ですから,ある条件 式が成り立っている間中処理を行います.以下に for の時に も使用した整数の和の while 文バージョンを示します.

      n = ARGV[0].to_i
      
      sum = 0         #和の値の初期化
      i = 1           #ループ内で使用する序数の初期化
      
      while i <= n    #iがnになるまで繰り返す
        sum += i      #和を自己代入で計算
        i += 1        #iの値を1増やす
      end
      
      puts sum
      

      違いは, while の行にある終了条件と,変数 i の初期値や増分を自 分で指定するところです.フローチャートは図1のようになります.

      図1 while ループのフローチャート

      基本構文は図2のようになり,for 文と同じです.

      図2 while ループの構成

      while ループでは増分が自分で指定できるので,for ループ で苦労した奇数の和のような演算 は楽になります.

      n = ARGV[0].to_i
      sum = 0
      i = 1
      
      while i <= n
        sum += i
        i += 2
      end
      
      puts sum
      

      偶数の例でも同じ様に書けることを確認してください.

      C言語の開発者である本人が書いたC入門の名著「プログラミング言語C」(共立出 版,1989)のループの最初に紹介されているのも実は for 文ではなく while 文なのですが,その例をRubyに翻訳 すると次のようなものです.

      lower = 0
      upper = 300
      step = 20
      
      fahr = lower
      while fahr <= upper
        celsius = 5 * (fahr - 32) / 9
        printf("%d\t%d\n", fahr, celsius)
        fahr += step
      end
      

      これは、アメリカで通常用いられている温度単位の華氏(Fahrenheit)により表 示された温度を摂氏(Celsius)に変換するプログラムです.構造をわかりやす くするためにいろいろと変数を定義したり用意したりしていますが,基本的な動 作はわかるでしょう.華氏について0度から300度まで20度おきに華氏と摂氏の 対応表を出力します.整数型で演算を行うためにちょっとした工夫がなされているのに気づくでしょう か?

      次の例は,コマンドライン引数として与えた数の平均値を求めるものです.引数 の個数が何個でも構いません.これが while ループの便利なところで す.

      sum = 0
      i = 0
      
      while value = ARGV.shift
        sum += value.to_f
        i += 1
      end
      
      printf "Averaged value is %1.2f.\n", sum / i
      

      shift という新しいメソッドが登場しています.これは,コマンドラ イン引数が納められている ARGV という配列の要素を一つず つ順番に取り出すことを行います.値をひとつずつずらして行くので shift という名前になっています.取り出した要素は次の行 で浮動小数に変換されて自己代入により加算され,同時に要素の個数 i も自己代入により計算されて行きます.最後は平均値の算 出です.

      相乗平均を求めるスクリプトに改良するとどうなるか,試してみて下さい.

    • 文字列とファイルの処理

      ターミナルで利用できるコマンドに cat がありました.これは,引数 として指定したファイルを画面に表示するものでしたが,これと同じ働 きをするスクリプトを作ってみましょう.

      while line = ARGF.gets
        print line
      end
      

      コマンドライン引数として与えた「値」を格納する定数は ARGV でし たが,「中身」を参照する定数が ARGF です.そして,gets というメソッドは改行文字 \n を行の区切りとして1行 ずつ読み込む動作を行います.ということは,このスクリプトはコマン ドライン引数として与えたファイルの中身を1行ずつ読み込んで line という変数に代入し,表示するものです.引数として与えた ファイルに中身がある限り,while は動作し続けます.最後 まで読み込んだら,ファイルが終了するのでループも終了します.

      次の例も終了条件が明確に決まっていない例です.コマンドライン引数を与えな いで実行すると,キーボードからの入力待ちになります.そこで,数字を入れて 行くと,入れた数字の和が50を越えたところで,ループから抜け出して終わりま す.

      sum = 0
      
      while value = gets
        sum += value.to_i
        if sum >=50
          printf "Sum comes to %d\n", sum
          puts "Stop inputting!\n"
          exit
        end
      end
      

      これを応用してもらうために昨年出した宿題が遠足のおやつスクリプトでした. これは,300円以内でお菓子を買うための計算スクリプトでした.

      remain = 300
      tax = 1.05
      
      while value = gets
        if (value.to_i*tax).floor > remain
          puts "No way."
        elsif (value.to_i*tax).floor == remain
          print "使った金額: ", (value.to_i*tax).floor, ",  買い物終了\n"
          exit
        else
          remain -= (value.to_i*tax).floor
          print "使った金額: ", (value.to_i*tax).floor, ",  残額: ", remain, "\n"
        end
      end
      

      消費税は1円未満の端数を切り捨てるので,そのためのメソッドである floor が新たに登場していますが,動作は難しくは無いでしょう.

      ちなみに四捨五入は round というメソッドにより実現できます.

    • 応用例

      次の例は単純にループを回すだけの処理を含む逆算タイマーです.動作環境に応 じて calibration の値を修正しないと正確に動作しないのが難点です が,それなりに動くはずです.

      totalmin = ARGV[0].to_i
      totalsec = totalmin * 60
      calibration = 0.475
      
      printf "残り時間\n"
      
      for t in 1..totalsec
        while Time.times.utime.to_f < t * calibration
        end
        minvalue = totalmin - 1 - (t - 1) / 60
        secvalue = (totalsec - t) % 60
        printf "0" if minvalue < 10
        printf "%d 分 ", minvalue
        printf "0" if secvalue < 10
        printf "%d 秒\n", secvalue
      end
      
      printf "Time up!\n"
      

      新しく出てきた時間に関するクラスですが,今は意味を知らなくても大丈夫です. 動作としては,コマンドライン引数として全体の時間を分で与えます.すると, 1秒毎に残り時間を表示して,残り時間が無くなったところで終了します.

      最後にちょっと難しい例を挙げておきましょう.次のスクリプトは三角関数 cos x の級数展開を行うスクリプトです.微積分で習ったマクロー リン展開が用いられているのが分かるでしょうか.

      include Math
      x = 0.5
      cosx = cos(x)
      errorlimit = 0.0001
      
      def fact(x)
        f = 1
        if x == 0
          return 1
        else
          for i in 1..x
            f *= i
          end
          return f
        end
      end
      
      n = 0
      sum = 0
      error = (cosx - sum) / cosx 
      
      while  error**2 > errorlimit**2
        sum += (-1)**n * x**(2*n) / fact(2*n)
        error = (cosx - sum) / cosx
        n += 1
      end
      
      printf "xの%d乗の項まで計算すると相対誤差が%1.3f%になります\n", 2*n, error*100
      

      動作は,実際の関数の値と展開して求めた値が,最初に設定した相対誤差0.01% 以内に収まるのは何次の項まで計算したときかを求めています.この計算では, 階乗を利用するので,特別に階乗を関数化して呼び出せるようにしています.こ れについては,今は理解できなくて構いません.

  3. 小テスト

    授業の残り20分くらいでAクラスBクラス別々の小テストを行いますので,アナウンス に注意してください.

  4. 宿題

    授業の最後にAクラスBクラス別々の宿題の案内も出しますので,アナウンスに注意してください.


目次ページに戻る