programming 4
プログラミング入門演習

補足

サブルーチン基礎

以下のプログラムを実行する前にどんな結果が得られるか考えよ. 実行したのち,予想が正しかったかそうか確認せよ.

サブルーチンの例 SubExp

class SubExp{
   public static void main(String args[]){
      int x;

      x = subA(2,3);
      System.out.println(x);
      x = subA(4,5);
      System.out.println(x);
   }

   static int subA(int a, int b){
      int c;

      c = a+b;
      return (c);
   }
}

プログラムの実行はメインルーチン main から行われる. メインルーチン中でサブルーチン呼出 (ここでは subA(2,3)) に当たると main の実行を一時中断してサブルーチン subA の実行が始まる.

サブルーチン中で retrun 文を実行すると, return の引数(上の subA では c )の値を計算した時点で サブルーチンの処理は終了し,もとのルーチンのもとの場所へ処理が戻る. このとき,return の引数の値でサブルーチンが書いてあった場所が置き変えられる. この例では,サブルーチン subA(x,y) が書いてあった所が c の値に 置きかえられる. つづいてもとのルーチンの残りの処理を実行する.

サブルーチンは引数を変えて何度も実行できる. サブルーチン内の変数は特別に宣言しない限り,他のルーチンの変数と干渉しないため, 同じ名前の変数を使っても問題ない.

講義メモ

課題プログラム続き

モジュールと全体設計

上で作成したメソッド isLeap と dateofym は, 全体の課題プログラムであるカレンダー表示プログラムの全体設計を 行う前に仕様を決定し,作成した. メソッドの仕様を決めてモジュール化を行う設計では, どんなメソッドを作成するかを決定することが重要である. isLeap, dateofym の例のようにプログラム全体の設計を行なう前に メソッドの仕様を決定してしまう場合と,全体設計を行ってから, あるいは設計と同時に必要なメソッドの仕様を決定する場合がある.

何度も使うことが予想できる比較的小さな機能は 先にメソッドの仕様を決めておくほうが効率がよい.

ここからはプログラム全体の設計を行ってから 必要なモジュールの仕様を考える. カレンダー表示処理の全体を以下のように設計する.

入力: int y, m

出力: 画面にその y 年 m 月のカレンダーを出力する.

全体の設計として,以下のように設計することができる. 最初に基準となる年の 1 月 1 日の曜日を調べておく. ここでは 2003 年 1 月 1 日(水)を基準とする. プログラムでは以下の順に処理を行えばよい. 曜日は整数型で表し,日曜が 0, 月曜が 1, … 土曜が 6 とする.

  1. プログラム全体の入力はカレンダーを表示させたい年,月で それぞれ int 型の変数 y, m として与えられている.
  2. y, m を入力とし,y 年 m 月 1 日の曜日を整数型で出力として返す メソッド getdayofym を実行し,結果を int dayofym に格納する.
  3. y 年 m 月が dayofym 曜日で始まることを利用して y 年 m 月 のカレンダーを出力する.

getdayofym はさらに以下のように設計することができる.

  1. y 年 1 月 1 日から y 年 m-1 月最終日の日数を計算する.
  2. y を入力とし,y 年 1 月 1 日の曜日を整数型で出力として返す メソッド getdayofy を実行し,結果と上の合計日数を足して 7 で割った余りを getdayofym の戻り値とする.

各モジュールの仕様

全体設計を行った結果下の仕様を満たすメソッドを作成すればよいことがわかっ た.

getdayofym
入力: int year, month,出力: y 年 m 月 1 日の曜日を整数型で出力として返す
getdayofy
入力: int year,出力: y 年 1 月 1 日の曜日を整数型で出力として返す

getdayofy メソッドの作成

getdayofym は getdayofy が完成しないとテストできないので getdayofy を先に作成する.

2003 年 1 月 1 日が水曜日(整数型の値 3 で表す)であることを 基準に y 年の 1 月 1 日を計算する. まず y が 2003 以上である場合を考える.

基準年 の 1 月 1 日から y-1 年の 12 月 31 日までの日数を計算すればよい. ループ変数 i を使って i = 基準年から i = y-1 年まで i 年の日数を加算してゆけば計算できる.i 年の日数は先に作成した isLeap を使えば 365 か 366 か決定できる. 基準年 2003 は変数 stdyear, 基準年の 1 月 1 日の曜日は変数 stdday に格納して使う. 基準年の 1 月 1 日の曜日に上で計算した日数を加え, 7 で割った余りを getdayofy の戻り値とする.

プログラム 29 (2003 以降にのみ対応)

class Example29{
   public static void main(String args[]){
      int y;

      for(y = 2003;y<=2100;y++){
         System.out.println(y+":"+getdayofy(y));
      }
   }

   static boolean isLeap(int year){
      boolean b;

      b = year % 400==0 || (year % 4 == 0 && year % 100 != 0);
      return (b);
   }

   static int getdayofy(int year){
      int stdyear = 2003;
      int stdday = 3;
      int i;
      int sum;

      sum = 0;
      for (i=stdyear; i < year; i++){
         if (isLeap(i))
            sum += 366;
         else
            sum += 365;
      }
      return((stdday+sum)%7);
   }
}

y が 2003 未満である場合は y 年から基準年の前年までの日数を計算してその値を 7 で割った余りを求め, 基準日との曜日のずれを求める.ずれが x でであったとすると, 基準日の曜日 - x が非負の数であればこの曜日が y 年の 1 月 1 日の曜日である. 負の数になった場合は 7 足せば正しい曜日になる.

2003 年以前にも対応するメソッド getdayofy は以下のとおりとなる.

プログラム部分 30

   static int getdayofy(int year){
      int stdyear = 2003;
      int stdday = 3;
      int i;
      int sum;

      if (year >= stdyear){
         sum = 0;
         for (i=stdyear; i < year; i++){
            if (isLeap(i))
               sum += 366;
            else
               sum += 365;
         }
         return((stdday+sum)%7);
      } else {
         sum = 0;
         for (i=year; i < stdyear; i++){
            if (isLeap(i))
               sum += 366;
            else
               sum += 365;
         }
         sum = stdday - (sum%7);
         if (sum < 0) sum+=7;
         return(sum);
      }
   }

getdayofym メソッドの作成

getdayofy が完成しているので getdofyermonth を作成するのは簡単である. その年の 1 月 から m-1 月までの日数の和を求め, getdayofy で求めた 1 月 1 日の曜日に加えて 7 で割った余りを求めればよい.

プログラム部分 31

   static int getdayofym(int year, int month){
      int i;
      int sum;

      sum = 0;
      for (i=1; i < month; i++){
         sum += dateofym(year, i);
      }
      return((getdayofy(year)+sum)%7);
   }

main メソッドの作成

指定した y 年 m 月の 1 日の曜日が getdayofym(y,m) で計算でき,その月が何日まであるかを dateofym(y,m) で計算できるのだから, main メソッドのプログラムの作成は容易である. 以下のプログラム 32 のカレンダー出力部分を完成させテストせよ.

プログラム 32

class Example32{
   public static void main(String args[]){
      int y =2003;
      int m =7;
      int dayofym;

      dayofym = getdayofym(y,m);

      < ここにカレンダー表示部分を記述する>
      
   }

   static boolean isLeap(int year){
      boolean b;

      b = year % 400==0 || (year % 4 == 0 && year % 100 != 0);
      return (b);
   }

      < ここに getdateofym を記述する>

   static int getdayofy(int year){
      int stdyear = 2003;
      int stdday = 3;
      int i;
      int sum;

      if (year >= stdyear){
         sum = 0;
         for (i=stdyear; i < year; i++){
            if (isLeap(i))
               sum += 366;
            else
               sum += 365;
         }
         return((stdday+sum)%7);
      } else {
         sum = 0;
         for (i=year; i < stdyear; i++){
            if (isLeap(i))
               sum += 366;
            else
               sum += 365;
         }
         sum = stdday - (sum%7);
         if (sum < 0) sum+=7;
         return(sum);
      }
   }

   static int getdayofym(int year, int month){
      int i;
      int sum;

      sum = 0;
      for (i=1; i < month; i++){
         if (i==1||i==3||i==5||i==7||i==8||i==10||i==12){
            sum += 31;
         } else if (i!=2){
            sum += 30;
         } else {
            if (isLeap(year))
               sum += 29;
            else
               sum += 28;
         } 
      }
      return((getdayofy(year)+sum)%7);
   }
}

Updated in July 10, 2003, schedule, Yamamoto Hiroshi