プログラミング/4

出典: CourseWiki

目次

繰り返し(for文)

実際のプログラムでは,次のようなある数の範囲に関する繰り返しは頻繁に現れます.

int i = 0;
while (i < 10) {
  処理;
  i++;
}

このため,このような構造はfor文を使って簡単に書けるようになっています. 上のプログラムはfor文を使うと次のように書けます.

int i;
for (i = 0; i < 10; i++) {
  処理;
}

この2つは全く同じ動作をします.

for文の書き方

for 文は次のように書きます.

for (初期化式; 条件式; 増分式) {
  処理
}

この文は以下のように実行されます.

  • 初期化式の実行
  • 条件式の判定 (trueならば次に進む.falseならばfor文終了)
  • 処理の実行
  • 増分式の実行
  • 条件式の判定 (trueならば次に進む.falseならばfor文終了)
  • 処理の実行
  • 増分式の実行
  • ...

増分式の部分は,for文の中の処理が終了してから実行されることに注意してください.

また,(while文と同じように)最初から条件式が成り立たない場合はfor文の実行はスキップされます(ただし初期化式の部分は実行されます).

なお,初期化式,条件式,増分式のそれぞれは,不要ならば書く必要はありません(ただし間のセミコロンは必要です). 条件式を省略すると true と見なされるので,for (;;) と書くことで無限ループを構成することが可能です.

初期化式,条件式,増分式には,式しか書けません.System.out.println() 等は書けないので注意してください. (代入文は式として扱われるので,代入文は書けます.また i++ のようなものも式です).

for文とbreak文

for 文の中でも break 文を使って for 文から脱出することができます.

初期化式の中での変数宣言

以下のように,初期化式の中で変数を宣言することができます.

for (int i = 0; i < 10; i++) {
  処理
}

for文の中だけで使う変数はこのように宣言しましょう.

(初期化式の中で宣言した変数(ここでは i) は for 文の中だけで有効(利用可能)です.for文の外側では使えません. #変数の有効範囲(スコープ)も参照して下さい.

for文とwhile文

for文とwhile文のどちらを使っても繰り返し処理は書けますが, どちらの書き方でも良いかというと,そういうわけでもありません. 先のwhile文にはちょっとした欠点があります.

  • i = 0 が,繰り返しに関係あるのかどうかが一目で分からない.
  • 繰り返しする処理が(何行にも渡って)長くなってくると,while の条件部分と最後の i++ の部分が離れてしまい,どのような繰り返しをするのか把握しにくい.

これに対して,for文はfor文の行だけをみて繰り返しの構造を把握することができるという点で優れています.

繰り返しにfor文を使うかwhile文を使うかの選択は趣味の部分もありますが,ある範囲の値について繰り返す処理は for文を使う方が良いでしょう.特に,上で挙げたような単純な繰り返しはfor文で書くことになっているので,while文で書いてあるとプログラムを読むときに「何か特別な意図があるのではないか」と思って引っかかる(読みにくい)という問題もあります.

なお,増加式と条件式の間に直接関係がないようなfor文は書くべきではありません.

演習(for文)

for文を使って以下のように0から10までの偶数を順番に表示するプログラム Even を書いてください.

0
2
4
6
8
10

演習(for文)の回答例


2重ループ

以下のような九九の表を表示するというプログラムを考えてみます.

1の段: 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の段: 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
...
9の段: 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

まず,大きく考えて1の段〜9の段を表示したいのですから,これはfor文を使ってiの段を表示するように書けそうです.

// 1の段〜9の段を表示するための繰り返し
for (int i = 1; i <= 9; i++) {
  System.out.print(i + "の段: ");
  // 横方向に i の段を表示する
}

さらに,横方向にiの段を表示する処理もfor文を使って書けそうです.

// 1の段〜9の段を表示するための繰り返し
for (int i = 1; i <= 9; i++) {
  System.out.print(i + "の段: ");
  // 横方向に i の段を表示する
  for (int j = 1; j <= 9; j++) {
    // i * j を表示する
  }
}

このように繰り返しの中に繰り返しがある構造を2重ループと言います.2つのfor文では,別々のカウンタ変数を使って繰り返しの数を数える必要があることに注意してください.

2重ループの中で break 文を使うと,一番内側のwhile文から脱出します.

演習(九九)

上の九九のプログラム Kuku を完成させなさい.

演習(九九)の回答例

演習(2重ループその1)

キーボードから整数 n を入力し,以下のような対角線の入った四角形(大きさは n × n)を書くプログラム Rectangle1 を書いてください.

Size: 10
*.........
.*........
..*.......
...*......
....*.....
.....*....
......*...
.......*..
........*.
.........*

2重ループを使います. ある条件が満たされたら "*" を,そうでなければ "." を表示します.

演習(2重ループその1)の回答例

演習(2重ループその2)

キーボードから整数 n を入力し,以下のような対角線の入った四角形(大きさは n × n)を書くプログラム Rectangle2 を書いてください.

Size: 10
*........*
.*......*.
..*....*..
...*..*...
....**....
....**....
...*..*...
..*....*..
.*......*.
*........*

演習(2重ループその2)の回答例

変数の有効範囲(スコープ)

変数は,例えば int a; のように書くと作り出すことができます. では,その変数はいつなくなるのでしょうか?

Javaのプログラムは,{}のペアで囲まれたブロックでできています. あるブロック内で作られた変数は,そのブロックの中だけで有効です. ブロックを出ると,その中で作られた変数は消滅し,それ以降使うことはできません.

{
  int i;
  i = 10;   // ここでは i が使える
  while (...) {
    int j;
    j = i;  // ここでは i と j が使える
    if (...) {
      int k;
      k = i + j;// ここでは i, j, k が使える
    }
    // ここでは i と j が使える
    k = 0;  // エラー!
  }
  // ここでは i が使える
  j = i;  // エラー!
}

ただし,上述したようにfor文の初期化部で作成した変数はfor 文の中だけで有効です.

for (int i = 0 ; i < 10 ; i++) {
  // i はこのブロックの中だけで有効
}

あるブロックの中で作った変数を,ブロックの外でも使いたくなった場合は,その変数を宣言する位置を変更する必要があります.

public static void main(..) {
  while (...) {
    double x = ...;
  }
  double y = x * x;  // エラー!
}

これは以下のように書き換える必要があります.

public static void main(..) {
  double x;  // 移動
  while (...) {
    x = ...;
  }
  double y = x * x;  // OK
}

配列

これまでの知識で,2つの数の和を求めたり,3つの数の最大値を求めたりするプログラムは作ることができるでしょう.では,ここで100個の数の和を求めよと言われたらどうしますか? 延々と長いプログラムを書かないといけないのでしょうか? でも,扱うデータの数が大きくなるに従って,プログラムもたくさん書かないといけないのでは,コンピュータを使っている 意味がありませんね.

そこで登場するのが配列(array)です.数学では名前が違うたくさんの変数を使う代わりに x1, x2, x3のように,変数に添字をつけて使っていますが,Java でも同じようなことができます.

  • 配列とは,同じ型の変数が複数並んだもの.
  • 複数のうちのどれかということは添字(index)で指定する.
  • 配列の中のそれぞれの変数を要素(element)と呼ぶ.
  • 配列を作るときに大きさ(要素の数)を決める.
  • 配列を一度作ったら,後から大きさ(要素の数)を変更することはできない.
  • 添字は0以上のint型の値.

例えば,5つの要素を持つint型の配列は,こんなイメージです.

画像:box_int_array.jpg

配列の作成

この配列を作るには,こんな風に書きます.

int[] x = new int[5];

これは,x[0] 〜 x[4] という5個のint型の変数を作り出します. 5個だからといってx[5]まで使えるわけではないことに注意して下さい(x[5]まで使ったら6個になってしまう).

同じように,50個の要素を持つ double 型の配列は次のようにして作ります.

double[] y = new double[50];
// 要素の数は変数で指定しても良い
int items = 50;
double[] yy = new double[items];

なお,int型,long型,double型等の配列を作ると,最初はそれぞれの要素には 0 (0.0) が入っています. この点は,普通の(配列ではない)変数と違うことに注意してください.(普通の変数は, 作っただけでは値が入っていない状態なので,一度も代入していない状態で参照するとエラーになるのでした). (変数の作り方・代入と参照参照)

初期値がある配列の作成

配列に何らかの値を入れておきたいときに,次のように書くのは大変です.

// JR阪和線の駅間距離の配列
double[] dist = new double[4];
dist[0] = 1.5; // 天王寺〜美章園
dist[1] = 1.5; // 美章園〜南田辺
dist[2] = 0.9; // 南田辺〜鶴ヶ丘
dist[3] = 0.8; // 鶴ヶ丘〜長居

このような場合,以下のように書くと配列の作成と初期値の代入を一度に行うことができます.

// JR阪和線の駅間距離の配列
double[] dist = {1.5, 1.5, 0.9, 0.8};
// 自動的に配列 dist の大きさは4になります.

配列の使い方

配列のそれぞれの要素は普通の変数と同じです.例えば

x[0] = Keyboard.intValue(); 
x[1]++;
x[2] = x[0] + 100;

のように書けます.

さて,沢山の箱を作ることができても,例えばその中身を全部 10 にしたいという場合に,いちいち

x[0] = 10; x[1] = 10; x[2] = 10; 

のように延々と書かないといけないならやっぱり大変です.でも,そんな心配は要りません. 配列の添字には変数を使うことができます.これが配列の利点です.

// x[0] 〜 x[99] の全てを 10 に設定する.
int[] x = new int[100];
for (int i = 0; i < 100 ; i++) {
  x[i] = 10;
}

配列の大きさ(.length)

配列の後に .length を付けるとその配列の大きさという意味になるので,上のプログラムは 次のようにも書けます.

// x[0] 〜 x[99] の全てを 10 に設定する.
int[] x = new int[100];
for (int i = 0; i < x.length ; i++) {
  x[i] = 10;
}

配列の全ての要素について繰り返す場合,.length を使って書きましょう. 意図が明確になりますし,後から配列の大きさを書き換えた場合にfor文の条件を書き直す必要がありません.

配列の表示

配列をSystem.out.println()などで表示すると,変な文字列が表示されます.

int[] x = {1, 2, 3};
System.out.println(x);

上のプログラムを実行すると,以下のような表示になります(@以降の数字は異なる場合があります).

[I@66848c

これは,Javaでは配列がオブジェクトであることに関連するのですが,ここではオブジェクトのことは置いておきましょう(詳しくは後でやります).

Javaで配列を簡単に表示するためには次のように書きます.

int[] x = {1, 2, 3};
System.out.println(java.util.Arrays.toString(x));

これを実行すると,以下の表示になります.

[1, 2, 3]

java.util.Arrays.toString というのが長ったらしいですが,後で出てくるimport文を使うと Arrays.toString と書くことができるようになります.

これ以外のフォーマットで表示したい場合は(両端のカッコや間のカンマを変更したいなど),for文などを使って自分で表示して下さい.

例外

よくある間違いとして,「配列の大きさより大きい添字を指定してしまう(あるいは0より小さい値を指定してしまう)」というものがあります.

public class OutOfBounds {
    public static void main(String[] args) {
        int[] x = new int[100];
        // x[99]までしか許されていないのだが…
        x[1000] = 1;
    }
}

このようなプログラムを実行すると,実行時にエラーが検出され,例外(Exception)が発生します.

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1000
at OutOfBounds.main(OutOfBounds.java:5)

例外というのは,プログラム実行中に何か異常な事態が発生したことを通知するためのメカニズムです. さまざまな「異常事態」に対応してたくさんの例外が予め定義されていて,java.lang.ArrayIndexOutOfBoundsException というのもその1つです.

java.lang.ArrayIndexOutOfBoundsException は,配列の添字が許されている範囲を越えているということを表す例外です. 表示から,この例外はプログラムの4行目で発生したこと,添字の値は1000だったということも分かります.

この例外が発生したら,プログラムに間違いがあるということですから,修正しないといけません.

演習(配列)

5個の実数をキーボードから入力し,配列に覚えた後,最後に配列の中身を表示するプログラム ArrayExercise を書いて下さい.

1: 10
2: 5.5
3: 20.5
4: 8
5: 9
10.0 5.5 20.5 8.0 9.0

ヒント: やりたいことは以下のようなこと.繰り返しが使えるところはfor文にする.

  • 配列 x を作る double[] data = new double[5];
  • 1: と表示
  • data[0] にキーボードから入力
  • 2: と表示
  • data[1] にキーボードから入力
  • ....
  • 配列の中身を表示する.

それができたら,入力した順番と逆順に表示するように追加して下さい.

1: 10
2: 5.5
3: 20.5
4: 8
5: 9
10.0 5.5 20.5 8.0 9.0
9.0 8.0 20.5 5.5 10.0

それもできたら,さらに,全部の合計と平均を求めてください.

1: 10
2: 5.5
3: 20.5
4: 8
5: 9
10.0 5.5 20.5 8.0 9.0
9.0 8.0 20.5 5.5 10.0
Total: 53.0
Average: 10.6

余裕がある人は,さらに入力された値が 0 ならば,入力をそこで終了するようにしてください(break文を使う). いくつ入力されるかわからないので,平均を求めるときに単純に5で割ってはいけません (入力された個数を数えておく必要があります).

1: 10
2: 5.5
3: 20.5
4: 0
20.5 5.5 10
Total: 36.0
Average: 12.0	(この場合,平均を求めるときに3で割ることに注意)

演習(配列)の回答例

課題4-1(距離マトリックス)

5つの駅(駅0から駅4まで)が直線状に繋がった鉄道路線がある.駅0から駅nまでの距離は以下のようになっている
(変更しても良い.また距離は整数(int型)として良い).

駅0〜駅0 : 0km
駅0〜駅1 : 1km
駅0〜駅2 : 3km
駅0〜駅3 : 4km
駅0〜駅4 : 9km

これから,駅間距離のマトリックスを計算して表示するプログラム DistanceMatrix を書け.

出力例:
   0 1 2 3 4
------------ 
0: 0 1 3 4 9 
1: 1 0 2 3 8 
2: 3 2 0 1 6 
3: 4 3 1 0 5 
4: 9 8 6 5 0 

ソースプログラム,実行結果,感想を提出すること.

ヒント

  • 駅の距離は次のような配列で記憶するとよい.
int[] dist = {0, 1, 3, 4, 9};
  • あとは2重ループを使って,駅iから駅jまでの距離を求めて表示する.
  • 距離を求める際に絶対値を使いたくなる.xの絶対値を求めるためには Math.abs(x) を使うとよい.

課題4-2(最大値と最小値)

5つの整数(1〜100まで)を入力し,その中の最大値と最小値を表示するプログラム MinAndMax を書け.

(余裕があるバージョン: 0 を入力することで入力の終りとします.10個までは入力できるようにして下さい)

Number: 10
Number: 40
Number: 90
Number: 80
Number: 40
最大: 90
最小: 10

ヒント

  • とりあえず入力された値を配列に記憶する.
  • 最小値・最大値を探す方法が問題.
    • 最小値の場合: いままでに出てきた最小の値を覚えるための変数minを用意し,配列の各要素とminを比較していく.もしminより小さい値があればその値をminにする.これを繰り返す.
  • まずは最小値だけを表示するプログラムを書き,うまく動いたら最大値も表示するように拡張しましょう.

課題4-3 (ヒストグラム)

(この課題は余裕がある人向けです)

学生の成績の分布をヒストグラムとして表示するプログラム Hist を書け.

0〜100の整数をキーボードから繰り返し読み込み(-1が入力されたら終わり),0点台(0〜9点),10点台(10〜19点),...,90点台, 100点の学生がそ れぞれ何人いるかをヒストグラムで表示する.

点数: 98 
点数: 22
点数: 94
点数: 100
点数: 45
点数: 92
点数: 29
点数: -1
0点台: 
10点台: 
20点台: **
30点台: 
40点台: *
50点台: 
60点台: 
70点台: 
80点台: 
90点台: ***
100点: *

ヒント

  • x点台の学生の数を覚えるために配列 hist を使います.
  • hist[x] が,x * 10点台の学生の数を格納するようにしてください.
    • つまり,hist[0] は0点台の学生の数,hist[1] は 10点台の学生の数,... とし,100点の学生の数は hist[10] に格納.
    • 配列には点数を入れておくのではなく,成績の分布を入れる
  • 点数から hist のどこに入れるか(つまり添字の値)を計算する必要があります.
  • 棒グラフ(*)の表示は,"*" を繰り返し表示すれば良い.
  • 最後の100点は「100点台」と表示しても構いません.
  • プログラムの構造は大体次のようになるでしょう.
  • 点数を入力する
  • 以下を繰り返す (無限ループ)
    • キーボードから点数を入力する
    • -1が入力されたら break
    • 点数から添字の値を計算して,hist[添字]++
  • 結果を出力する
    • x点台を表示するための繰り返し(x = 0, 1, ..., 10)
      • x*10 点台: と出力.
      • "*" を hist[x] の数だけ出力する.
      • 改行する.

Copyright (C) 2002-2015 Kota Abe, Osaka City University

ナビゲーション