解説 10月(3)

今回の学習項目です。


テスト(検証)の重要性

少し複雑なプログラムを一度に完成させることはできません。プログラムは多くの部品から構成される複雑な構造物です。プログラムを構成する一つ一つの部品が正常に動作しない限り、プログラム全体が正常に動作しないのは当然です。作った部品がそのまま正常に動作することはまれです。プログラムにバグがあるのは普通です。文法間違いのような単純なバグから、なかなか見つけにくい論理に関係するバグまで、いろいろな種類のバグを人は作り出します。人がバグを作り出すことを当然のこととして、それを視野に入れてプログラムを作成していく必要があります。

バグの有無はなるべく早い段階でチェックし、発見し、取り除いておく必要があります。全体を作ってから、バグを治すのは得策ではありません。実際、全体を作ってからバグを取ろうとしても、どこにバグがあるのか分からず、結局、部品に分けてチェックしていかざるを得ないからです。はじめから、部品に分けてチェックすれば、もしその部品が働かなければ、その部品の中に問題があることが分かりますから、バグのありそうなところが比較的狭い範囲で特定できることになり、結果的にバグは比較的見つかりやすくなります。次に出るバスの時刻表を表示するプログラムの例で検討してみましょう。

テストの基本は、いろいろな具体例で、きちんと動作するか確かめてみることです。例えば、現在時刻が9時20分のときの動作を調べます。全部の場合を調べ尽くすことはできませんから、要所、要所を調べます。どんなところが要所かを判断しなければなりません。典型的なところから外れるところも調べなければなりません。

課題1

課題1-1-1

適切に動作するかどうかを要所要所で具体例を作って調べることにした。 以下の二つの場合以外に、適切に動作することを確かめるべき要所が最低二つある。 それがどこかを考えて、具体例をあげなさい。また、なぜそれが要所になるのか、理由も述べなさい。

・現在時刻が9時20分のとき
   => 典型的な時刻の例。

・現在時刻が9時55分のとき
   => その時間の最後の発車時刻を過ぎた場合。
      次の時間の最初の発車時刻を返す必要がある。

...

課題1-1-2

次のプログラムがきちんと動作するかどうか確かめ、結果を報告しなさい。 実行結果だけでなく、プログラムが正しく動作したかどうかも、上であげた要所ごとに検証して報告すること。
下のプログラムでやっているような形で、プログラムの中で、変数の値を上書きしてテストするのが一つの方法になる。 以下のプログラムと、次の時刻表データを使うこと。

bustimetable0.txt

※ Windows環境下で実行する場合は、$KCODEに "s" を代入する必要があります。

$KCODE = "e"

class Bustime
  def initialize(bustable)
      @file = bustable
      fo = open(bustable, "r")
      @table = fo.readlines
      fo.close
  end # def of initialize
  def jikkou
    #現在時刻の取得。
    now = Time.now
    hour_now = now.hour
    minute_now = now.min
    ###テスト用の差込み。現在時刻の時と分の値を、テストのために変更する。
    hour_now = 9
    minute_now = 20
    ###

    #現在時刻が始発以前の場合の処理。
    if(hour_now < 8)
      line = @table[0]
      time_ary = line.split(/,/) 
      hour = time_ary[0]
      minute = time_ary[1]
      str = "次に出るバスの時刻は、" + hour.to_s + "時" + minute.to_s + "分です。\n"
      return str
    end

    #次の発車時刻を探す。
    for line in @table
      time_ary = line.split(/,/) 
      hour = time_ary[0].to_i
      if(hour == hour_now)
        for j in  time_ary
          minute = j.to_i
          if(minute > minute_now)
            str = "次に出るバスの時刻は" + hour.to_s + "時" + minute.to_s + "分です。\n"
            return str
         end # if
         end # for j
      end # if
    end # for line

    #次の発車時刻がみつからなかった場合の処理。
    return "もう今日のバスはありません。\n"
  end
end

bt = Bustime.new("bustimetable0.txt")
print bt.jikkou

プログラムの改善:適切な部品化

プログラミングで大事なことは、単に「動く」プログラムを作ることではなく、よいプログラムを作ることです。よいプログラムとは、高速に動くということも大事ですが、分かりやすく、メインテナンスがしやすいということも大事です。現実の場面で役に立つ複雑なプログラムでは、すぐに完成品ができるというわけにはいきません。いろいろなバグがあるのはむしろ普通です。バグの発生を最小限に抑え、バグがあってもすぐに見つけて修正できるようなプログラムを作っていくことが必要になります。そのためには、プログラムは分かりやすい論理を採用し、それを明瞭に表現してあることが必要です。分かりやすい論理を採用してあれば、機能を追加することも、バグを持ち込まずに容易にできます。 分かりやいロジックが何かは一概には言えません。よい部品化がなされていることも大事ですが、対象にしている世界が簡潔に表現されていることも大事です。このことを次に今やっている次に出るバスの時刻を表示するプログラムの例で考えて行きましょう。

・バスの時刻表の情報はどう表現するのがよいか。

自然な表現は多くの皆さんが採用した、何時を表す数を左端におき、その右に、分を置いていくというものでしょう。これが分かりやすいのは、時でまとめるグルーピングが自然だからでしょうし、このことに関係して、人が次に出るバスを見つけやすいということがあるからでしょう。

8 10 25 45 54     #8時台の発車時刻
9 10 25 45 54     #9時台の発車時刻
10 18 28 40 54    #10時台の発車時刻
...

しかし、プログラムを作るという立場からすると、これは、それほど分かりやすいものであるかどうかはわかりません。実際、時と分に分かれているために、いろいろな場合を検討する必要がでてきます。

このプログラムの本質的な点は、時刻表に順番に並んでいるバスの出発時刻を早い方から一つ一つ見ていき、時刻表の時刻が現在の時刻よりも大きくなった時点で、終了するというものです。次のように一列に並んでいるともう少し分かりやすいかもしれません。

8 10
8 25
8 45
8 54
9 10
9 25
9 45
9 54
10 18
10 28
10 40
10 54
...

少し分かりやすくなったかもしれませんが、これでもまだ、現在時刻と時刻表の時刻の比較のところが複雑です。これは、時と分に分かれているからです。時刻の表現を工夫すると、このプロセスを簡単にすることができそうです。

一つの方法は、時刻を「午前0時から何分経過したか」という形で表現してしまうことです。現在時刻と時刻表の時刻が両方とも「分」という一つの数になりますから、時と分を別々に比較する必要がなくなります。そうすることで、いろいろな場合の検討がいらなくなり、バグの原因になる、複雑なifによる分岐を単純なものにすることができます。

490    #8時10分
505    #8時25分
525    #8時45分
534
550
565
585
594
...

課題1-2-1

上のように、時刻を午前0時からの経過分数で表現する方法を用いて、バスの発車時刻のプログラムを作成しなさい。 時刻表のデータは、時と分が一列に並んだ下のものを使うとよい。 経過分数で時刻を表現したファイルを自分で作ってもよいが、下の時刻表ファイルをプログラムの中に読み込み、経過分数に変換してもよい。

bustimetable.txt

課題1-2-2

Timeオブジェクトを使って時刻を表す方法もあります。 Timeクラスで作られた時刻のオブジェクトは、数の場合と同様に、互いの大小を比較することができます。

hour = 8
min = 10

t1 = Time.now                                           #現在時刻のTimeオブジェクトを生成。
t2 = Time.mktime(t1.year, t1.month, t1.day, hour, min)  #同じ日付の8時10分のTimeオブジェクトを生成。

p t1 < t2
false    #10時に実行した場合。10時は8時10分より前ではない。

これを使うことで、プログラムがより自然な形になり、安定したものになります。

Timeクラスを使って、プログラムを作り直しなさい。 上のようにmktimeメソッドを使用すれば、任意の時刻のTimeオブジェクトを作り出すことができる。

プログラミングIIIのホームに戻る