| 目次| 1234567891011121314
| 資料| 予定| 課題| 宿題|

制御構造(2)

繰り返し-for-

ここまでで,Java 言語では 条件分岐 というやや複雑な計算(制御)が実現で きることが理解できたかと思う.Java 言語ではさらに,同じ計算の繰り返し, つまり 大量の繰り返し計算 を行うことができる.そのための手続きがいくつ か用意されているが,その中で最もよく使われる for 文 についてまず学習する.

for 文とは何か?

まずは次のプログラムを眺めてみよう.
public class Count {
   public static void main(String[] args) {
      System.out.println(0);
      System.out.println(1);
      System.out.println(2);
   }
}
上のプログラムは,0,1,2 の 3 個の数字を順次表示するだけのプログラム である.3 個の数字を表示するだけなら何ら問題はないが,100 個だとか 1000 個だとかの数字を表示するプログラムを書くのは抵抗があるだろう.こ のように,繰り返し回数がわかっている場合に,ある処理(計算)を繰り返し 行う場合には,下のプログラムのように, for 文を用いてプログラムを書く ことができる.
public class Count2 {
   public static void main(String[] args) {
      int i;
      for (i = 0; i < 3; i=i+1) {
         System.out.println(i);
      }
   }
}
上のプログラムで,0,1,2 の 3 つの数字を順次表示する部分は,
   for (i = 0; i < 3; i=i+1) {
      System.out.println(i);
   }
である. for 文は,中括弧 { } で括られた部分の処理を,「条件を満たす限 り」繰り返し行う.for のすぐあとの括弧 ( ) で括られた部分は,繰り返し の制御をしている部分である.一般的に書くと for 文の構造は下記の通りで ある.なお,初期化と条件式の間,および条件式と"次の一歩"の間には,セミ コロン (;) が必要なので注意すること.
     for (初期化; 条件式; 次の一歩) {
        繰り返す処理
     }
  
初期化
初期化は,繰り返しの準備をする部分で,繰り返しの前に一度だけ評価される. 上のプログラムでは,i = 0 がそれに当たり,i を 0 で初期化している.
条件式
条件式は,繰り返しを続ける条件を表現した部分で,ここに書いた条件が満た されている限り,繰り返して中括弧 { } で括られた部分の処理を行う.上の プログラムでは,i < 3 がそれに当たり,(制御)変数 i が 3 未満(2 以 下)の間,繰り返しが行われている.
次の一歩(実行式)
次の一歩は,繰り返す処理が 1 回分終った後に常に実行される部分で,繰り返 しを次に進めるための処理が書かれる.上のプログラムでは,i=i+1 がそれに当 たり,「変数 i の値を 1 増やす」ことを意味する.
通常,繰り返しの初期値<繰り返しの終値であって,初期値>終値の場合 には「繰り返す処理」は一度も実行されない.また初期値=終値の場合には, 「繰り返す処理」は一度だけ実行される.また,(制御)変数はふつう int 型 であり,初期値と終値は(制御)変数と同じ型である.

注意点

下記に for 文の例をいくつか列挙する.
  int i;
  for (i = 0; i < 3; i=i+1) {
     System.out.println("Hello Java World!");       ←同じ表示を3回繰り返す
  }

  int j;
  int sum=0 ;
  for (j = 1; j < n + 1; j=j+1) {
     sum = sum + j;              ←1〜nまでの和
  }
  
「条件式」と「次の一歩」の部分は以下のように省略される場合が多い.
  for (int i = 0; i < 3; i++) {
     System.out.println("Hello Java World!");       ←同じ表示を3回繰り返す
  }

  for (int j = 1; j < n + 1; j++) {
     sum = sum + j;                  ←1〜nまでの和
  }
  
変数の宣言と初期化を「条件式」の中で"int i = 0"とまとめて記述する. 「次の一歩」で書かれている"i++"は,"i=i+1"の省略した書き方でインクリメ ント演算子と呼ばれる.同様の演算子で"i=i-1"を意味するデクリメンタル演 算子という演算子も用意されていて"i--"と書く.

複数の処理,2重化

下記の例のように,繰り返し処理に複数の(処理)を書くことが可 能である.また,for 文を二重(多重)にして利用することも可能である.
public class Count3 {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.print(i + "の2乗は" + (i * i) + "で、");
            System.out.println("3乗は" + (i * i * i) + "です。");
        }
        System.out.println("end");
    }
}
public class DrawGraph1 {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.print(i + ":");
            for (int j = 0; j < i; j++) {
                System.out.print("*");
            }
            System.out.println("");
        }
    }
}
[onisi@m449 ~/Java] java DrawGraph1
0:
1:*
2:**
3:***
4:****
5:*****
6:******
7:*******
8:********
9:*********
 外側のfor内側のfor
プログラム
for(int i=0 ; i<10; i++){
for(int j=0 ; j<i; j++){
ループ用の変数int iint j
繰り返しの回数 iが0,1,2,....,9の10回繰り返す jが0,1,2,..,i-1のi回繰り返す

配列

変数から配列へ

3 人の学生からなるクラスで試験を行った結果,学生証番号 1 の人の点数が 63 点,学生証番号 2 の人の点数が 90 点,学生証番号 3 の人の点数が 75 点だったとしよう.各々の点数を no1,no2,no3 という変数で表して,その 平均点(double 型変数 heikin に格納)を計算するプログラムは以下の通り である.
  public class Heikin {
     public static void main(String[] args) {
        int no1, no2, no3;
        double heikin;

        no1 = 63;
        no2 = 90;
        no3 = 75;

        heikin = (no1 + no2 + no3) / 3.0;

        System.out.println("学生証番号 1 の人の点数は" + no1);
        System.out.println("学生証番号 2 の人の点数は" + no2);
        System.out.println("学生証番号 3 の人の点数は" + no3);
        System.out.println("平均点は" + heikin);
     }
  }
  
上のプログラムでは 3 人の学生の点数のために 3 つの変数を用意したが,例 えばクラスの人数が 100 人だったら 100 個,別々に変数を用意する必要があ る.そもそもこの 3 つの変数にはすべて「点数を表す」という共通の目的が あるので,その共通性を利用した変数を宣言・利用したい.Java 言語では, 共通の目的をもつ複数の変数を番号を付けてまとめるという仕組みが 用意されている.それを配列と呼ぶ.配列を使って上のプログラムを書き換え ると以下のようになる.
  public class Heikin2 {
     public static void main(String[] args) {
        int[] tensu;
        double heikin;

        tensu = new int[3];
        tensu[0] = 63;
        tensu[1] = 90;
        tensu[2] = 75;

        heikin = (tensu[0] + tensu[1] + tensu[2]) / 3.0;

        System.out.println("学生証番号 1 の人の点数は" + tensu[0]);
        System.out.println("学生証番号 2 の人の点数は" + tensu[1]);
        System.out.println("学生証番号 3 の人の点数は" + tensu[2]);
        System.out.println("平均点は" + heikin);
     }
  }
  

配列用の変数の宣言

    int[] tensu;
上のプログラムの 3 行目では,まず,配列用の変数を宣言している.はじめ の int[ ] は int 型の配列を意味し,次の tensu はこの配列の名 前を表している(なお [ ] を型の後にではなく配列名の後につけて, int tensu[ ] と宣言することも可能である).

配列を確保する

    tensu = new int[3];
上のプログラムの 6 行目では,int 型の変数 3 つ分の領域がメモリ上に確 保されている.そして,その領域(実際に配列がある領域)を変数 tensu が 指すようになる. new int[3] の中括弧の中の 3 は配列の要素の数を示す. 配列の要素の数を,しばしば配列の長さ(length)と呼ぶ(配列の長さは 0 以 上でなければならない).

ここで new は,新しくメモリの領域を確保する際に使う Java の予約語で,

  new 型名[要素の数]
  
という形式を取る(「型名」で指定された型の変数が「要素の数」で指定され た個数だけ確保される).

配列の要素に代入する

        tensu[0] = 63;
        tensu[1] = 90;
        tensu[2] = 75;
上のプログラムの 7 〜 9 行目では,配列の各要素に各々の点数を代入して いる.プログラムを見ればわかるように,Java 言語の配列の要素は必ず 0 番目から始まる(1番目からではない!)ことに注意する必要がある.従っ て,配列の長さ(要素の数)が 3 の配列 tensu の場合には,使える配列の要 素は tensu[2] までとなる.ここで要素の番号を表す [ ] 内の数を添字 (index) と呼ぶ.配列の要素への代入の一般形は,
  配列名[添字] = 代入する値;
  
となる.

配列の要素を参照する

        heikin = (tensu[0] + tensu[1] + tensu[2]) / 3.0;
配列の要素の値を参照する場合には,
  配列名[添字]
とする.上のプログラムでは,11 行目で平均の計算をする際と 13 〜 15 行 目で各人の点数を表示する際に,配列の各要素の値を参照している.

配列と変数の比較

配列と変数を比較すると以下のようになる.
  int[ ] 型の配列 int 型の変数
宣言 int[ ] tensu; int no1, no2, no3;
確保 tensu = new int[3]; 不要
代入 tensu[0] = 63;
tensu[1] = 90;
tensu[2] = 75;
no1 = 63;
no2 = 90;
no3 = 75;
参照 tensu[0]
tensu[1]
tensu[2]
no1
no2
no3

配列と繰り返し

配列を使うメリットの一つは,配列の添字を変数として扱える点である. Heikin2 のプログラムで,合計点を求める部分を配列の添字を変数化して書き 換えると,以下のようなプログラムになる.
  public class Heikin3 {
     public static void main(String[] args) {
        int[] tensu;
        int goukei;
        double heikin;

        tensu = new int[3];
        tensu[0] = 63;
        tensu[1] = 90;
        tensu[2] = 75;

        goukei = 0;
        for (int i = 0; i < 3; i++) {
           goukei = goukei + tensu[i];
        }
        heikin = goukei / 3.0;

        System.out.println("学生証番号 1 の人の点数は" + tensu[0]);
        System.out.println("学生証番号 2 の人の点数は" + tensu[1]);
        System.out.println("学生証番号 3 の人の点数は" + tensu[2]);
        System.out.println("平均点は" + heikin);
     }
  }

length

上記のプログラムで,クラスの人数を 3 から 5 に増やしてみよう.その際に, Heikin3 プログラム 13 行目の for 文中の 条件式 と,16 行目の「合計点を 人数で割る」式の 2 ヵ所において,クラスの人数も 3 から 5 に増やす必要 があるが,人数が増えるごとにいちいち書き換えるのは面倒である.このクラ スの人数とは配列の要素の数,つまり配列の長さなのだから,配列の長さを得 るメソッドがあれば,それを上述の 2 ヵ所に書いておけば良いことになる. 配列の長さを得るためのメソッドとしては length があり,配列名 .length という形式で用いる(.はドット演算子と言い,オブ ジェクトのメソッドを呼び出すときに用いる).ここでの配列名は tensu な ので,tensu.length として呼び出すことが可能である.そのように書 き換えたのが以下のプログラムである.
  public class Heikin4 {
     public static void main(String[] args) {
        int[] tensu;
        int goukei;
        double heikin;

        tensu = new int[5];
        tensu[0] = 63;
        tensu[1] = 90;
        tensu[2] = 75;
        tensu[3] = 45;
        tensu[4] = 81;

        goukei = 0;
        for (int i = 0; i < tensu.length; i++) {
           goukei = goukei + tensu[i];
        }
        heikin = (double)goukei / tensu.length;

        System.out.println("学生証番号 1 の人の点数は" + tensu[0]);
        System.out.println("学生証番号 2 の人の点数は" + tensu[1]);
        System.out.println("学生証番号 3 の人の点数は" + tensu[2]);
        System.out.println("学生証番号 4 の人の点数は" + tensu[3]);
        System.out.println("学生証番号 5 の人の点数は" + tensu[4]);
        System.out.println("平均点は" + heikin);
     }
  }
lengthとlenght()を間違えた
配列の長さはlengthと言う(実は,メソッドではなく)フィールドで得ら れます.文字列の長さは lengh()と言うメソッドで得られます.これを 間違えるとコンパイルエラーになります.

型変換と Cast 演算子

上のプログラムで注意しなければならないのは,18 行目の平均を求めている 式である.Heikin3 プログラムでは,
  heikin = goukei / 3.0;
  
のように書いていた.3.0 を 3 と書いた場合,変数 goukei は int 型なので, 右辺の割算の結果は小数点以下が切り捨てられてしまう.それを避けるために 3.0 という実数型を指定した(このように整数型と実数型とが混在する場合に は,整数型のデータは自動的に実数型に型変換される.参考までに自動的な型 変換の規則は以下の通りである).
  1. 式の中で一番サイズの大きい型に合わせる.サイズは小さい順に, byte < short, char < int < long < float < double である.
  2. 整数型と実数型の場合,データサイズとは関係なく整数型のデータは 自動的に実数型に変換される.
  3. 文字型と他の数値型の型変換では,文字は 0 から 65535 の範囲の正 の整数としてのみ扱われる.
  4. 論理型は別の型に型変換できない.
しかし Heikin4 プログラムの 18 行目では,割算の分母として実数型の定数 を指定できず(配列の長さは整数なので),右辺の割算の結果は小数点以下が 切り捨てられてしまう.そこで変数 goukei の値を明示的に型変換する必要が ある.それには Cast 演算子 を用いることができる.その一般形は,
  (型名)式
  
である.従って Heikin4 プログラムの 18 行目では,変数 goukei を double 型にキャストして,
  heikin = (double)goukei / tensu.length;
  
としている.

配列の初期化

これまでは,配列を宣言・確保してから要素への代入を行っていたが,宣言と 同時に確保と要素への代入を行うこともできる.その一般形は,
 配列の型[ ] 配列名 = { 要素, 要素, 要素, …, 要素, 要素 };
  
である.従って Heikin4 プログラムを以下のように書き替えることが可能で ある(個人の点数表示も改良する).
  public class Heikin5 {
     public static void main(String[] args) {
        int[] tensu = { 63, 90, 75, 45, 81 };
        int goukei = 0;
        double heikin;

        for (int i = 0; i < tensu.length; i++) {
           goukei = goukei + tensu[i];
           System.out.println("学生証番号 "+i+" の人の点数は" + tensu[i]);
        }

        heikin = (double)goukei / tensu.length;
        System.out.println("平均点は" + heikin);
     }
  }
  

2 次元配列

Java 言語では「配列の配列」を作ることも可能である.これを2 次元配列と 呼ぶ.例えば,あるクラスの英語・数学・国語の点数を扱う場合には,各行が 科目別の点数データであるような matrix を考えれば良いわけで,これを 2 次元配列で表現することができる.学生 5 人の英語の各点を 63, 90, 75, 45, 81,数学の各点を 85, 100, 95, 80, 90,国語の各点を 100, 100, 100, 100, 100 とするとき,これを 2 次元配列 kamoku_tensu を用いて以下のよう に書くことができる.
  public class KamokuHeikin {
     public static void main(String[] args) {
        int[][] kamoku_tensu = { { 63, 90, 75, 45, 81 },
                                 { 85, 100, 95, 80, 90 },
                                 { 100, 100, 100, 100, 100 },
                               };

        for (int i = 0; i < kamoku_tensu.length; i++) {
           int goukei = 0;
           for (int j = 0; j < kamoku_tensu[i].length; j ++) {
              System.out.print("\t" + kamoku_tensu[i][j]);
              goukei = goukei + kamoku_tensu[i][j];
           }
           System.out.println("\t| " + (double)goukei / kamoku_tensu[i].length);
        }
     }
  }
上のプログラムでは 3 〜 6 行目で,2 次元配列 kamoku_tensu が初期化され ている.8 行目からの for 文の中で配列の長さが使われているが,2 次元配 列 kamoku_tensu は 3 つの要素からなる 1 次元配列 kamoku_tensu[ ] の各 要素がさらに,5 つの 要素からなる 1 次元配列(列成分)となっているよう な配列なので,kamoku_tensu の長さは 3,各 kamoku_tensu[i](0 ≦ i ≦ 2) の長さは 5 となる.従って,上のプログラムの外側の for 文では各 kamoku_tensu[i](列ベクトル)に対して処理を行い,内側の for 文では kamoku_tensu[i](列ベクトル)の各要素 kamoku_tensu[i][j] に対して処理 を行っていることになる.このように 2 次元配列の各要素の値は kamoku_tensu[i][j] で参照可能である.なお,上のプログラムの中の \t は, 桁そろえを行うためのタブ文字を表す.
また 2 次元配列において,各要 素となっている 1 次元配列の長さが一定でなくても良い.

課題

  1. 1から100までの整数のうち3の倍数の和を求めるプログラム(for文を使って)を書きなさい.
  2. 数列 A1=1,Ak=2*Ak-1+3について
    (for文を用いること)
  3. 月を入力すると英語名を答えるプログラム
    *注意*(上級向) 例外処理の必要はありません (因みに強制終了は"System.exit(0);"とする).
    public class Month {
        public static void main(String args[]) {
    
    	// 文字列の配列型の変数 mnames を宣言し,{}で囲まれた初期値を入れる.
    	String[] mnames={"January","February","March",
    			 "April","May","June",
    			 "July","August","September",
    			 "October","November","December"};
    
          // 上の配列を利用しここから下を考えてください.
          
          
          }
    }      
          
    実行例
    [onisi@m449 ~/Java] java Month
    月(1-12)を入力してください9
    9月はSeptemberです.
          
  4. 次のような九九の表を画面に表示するプログラムを書きなさい. [二重のfor文]
    [onisi@m449 ~/Java] java DrawKukuTable
    1*1=1, 1*2=2, 1*3=3, 1*4=4, 1*5=5, 1*6=6, 1*7=7, 1*8=8, 1*9=9, 
    2*1=2, 2*2=4, 2*3=6, 2*4=8, 2*5=10, 2*6=12, 2*7=14, 2*8=16, 2*9=18, 
    3*1=3, 3*2=6, 3*3=9, 3*4=12, 3*5=15, 3*6=18, 3*7=21, 3*8=24, 3*9=27, 
    4*1=4, 4*2=8, 4*3=12, 4*4=16, 4*5=20, 4*6=24, 4*7=28, 4*8=32, 4*9=36, 
    5*1=5, 5*2=10, 5*3=15, 5*4=20, 5*5=25, 5*6=30, 5*7=35, 5*8=40, 5*9=45, 
    6*1=6, 6*2=12, 6*3=18, 6*4=24, 6*5=30, 6*6=36, 6*7=42, 6*8=48, 6*9=54, 
    7*1=7, 7*2=14, 7*3=21, 7*4=28, 7*5=35, 7*6=42, 7*7=49, 7*8=56, 7*9=63, 
    8*1=8, 8*2=16, 8*3=24, 8*4=32, 8*5=40, 8*6=48, 8*7=56, 8*8=64, 8*9=72, 
    9*1=9, 9*2=18, 9*3=27, 9*4=36, 9*5=45, 9*6=54, 9*7=63, 9*8=72, 9*9=81, 
          

| 目次| 1234567891011121314
| 資料| 予定| 課題| 宿題| Last modified: Wed Oct 26 13:20:05 JST 2016