プログラムの基本的な構造で最も重要なものは、条件分岐と繰り返しです。この章では、条件分岐について解説します。繰り返しについては次の章で解説していきます。
条件分岐と繰り返しを理解し駆使できるようになると、プログラムの幅が一気に広がります。
ここまで紹介したプログラムは、いずれも「mainメソッドの先頭から最後までをすべて順番に実行する」というものでした。この章では条件分岐というものを学習します。条件分岐によって、プログラムの一部の記述を飛ばして実行することができるようになります。
条件分岐を実現するための構文には、if文とswitch文があります。この項では、if文について学習していきます。
if文は予約語ifを使用します。if文の最もシンプルな形は以下の通りです。
if(boolean型となるような式) {
// 条件が trueとなったときに実行する処理
}
ifのあとにカッコ「( )」を記述します。その中には演算結果が boolean型になるような式を記述します。そして、その演算結果が trueとなったとき、「{ }」でくくられた処理が実行されます。演算結果が falseのときは、「{ }」でくくられた処理は実行されません。
サンプルプログラムを以下に挙げます。なお、このプログラムでは MySystemライブラリを使用します。ライブラリのダウンロードおよび利用方法については、Math.randomメソッドと自作ライブラリを参照してください。
E101/MySystem.java(ライブラリをそのまま利用します)
E101/E101.java
/**
* if文を確認します。
*/
public class E101 {
/**
* メインメソッド。
* @param args 引数
*/
public static void main(String[] args) {
int price;
System.out.println("ご来店ありがとうございます。");
// 1,000~1,999円までのランダム値を priceに代入する
price = 1000 + (int)(Math.random() * 1000);
System.out.println("お会計は " + price + "円になります。");
int input;
input = MySystem.in.getInt("レジ袋はご入用ですか、1枚 5円です。(1=はい)");
if(input == 1) {
// レジ袋が必要ならば 5円を加算して、会計をもう一度表示する
price += 5;
System.out.println("お会計は " + price + "円になります。");
}
System.out.println("ご来店ありがとうございました。");
}
}
実行結果の例は以下の通りです。なお、ランダム値を使用しているので、実行結果は実行の都度変わります。また、キーボードからの入力を必要とします。
ご来店ありがとうございます。 お会計は 1076円になります。 ポリ袋はご入用ですか、1枚 5円です。(1=はい)? 1 お会計は 1081円になります。 ご来店ありがとうございました。
ご来店ありがとうございます。 お会計は 1861円になります。 ポリ袋はご入用ですか、1枚 5円です。(1=はい)? 0 ご来店ありがとうございました。
キーボードから「1」を入力したときには、変数 inputの値が 1になります。その結果、22行目のinput == 1は trueになります。したがって、 { }の中身、すなわち 23~24行目が実行されます。キーボードから「1」以外の数を入力したときは、22行目のinput == 1は falseになります。そのため、23~24行目は実行されません。
先の例では、演算結果が trueのときのみ処理が実行されました。trueのときと falseのときとでそれぞれ異なった処理をしたい場合には、予約語elseを使用します。
if else構文は次の形式です。
if(boolean型となるような式) {
// 条件が trueとなったときに実行する処理
} else {
// 条件が falseとなったときに実行する処理
}
if else構文を用いて以下の仕様のプログラムを作成します。
上記の仕様を満たしたプログラムは以下の通り。
E102/MySystem.java(ライブラリをそのまま利用します)
E102/E102.java
/**
* if else構文を確認します。
*/
public class E102 {
/**
* メインメソッド。
* @param args 引数
*/
public static void main(String[] args) {
int age;
age = MySystem.in.getInt("年齢を入力してください");
if(age < 20) {
System.out.println("未成年です");
} else {
System.out.println("大人です");
}
}
}
実行結果の例は以下の通り。
年齢を入力してください? 15 未成年です
年齢を入力してください? 38 大人です
自分が作ったプログラムが、自分の計画した通りに動かないことがあります。それを一般的にはバグと言います。発生したバグに対して、原因を追究し解消する作業のことをデバッグと言います。ただ、バグが発生してから対応するというのでは手遅れとなる事態もあったりします。
自分が作ったプログラムに自信がないならば、テストを実施しましょう。テストはホワイトボックステストとブラックボックステストに分類されますが、今回はブラックボックステストのひとつである境界値チェックをしてみようと思います。具体的には以下の 3つについて実行結果を確認します。
つまり、20という境界に対してチェックを行います。この 3つが正しく動作すれば、恐らくこのプログラムは正しく動作するだろうと判断するということになります。確認した結果は以下の通り。
年齢を入力してください? 19 未成年です
年齢を入力してください? 20 大人です
年齢を入力してください? 21 大人です
はい、大丈夫ですね。恐らくこのプログラムは正しく動作するだろうと判断できます。
自分が作成したプログラムは、その都度テストする癖をつけるとバグの発生を抑えることができます。プログラムが巨大になるほどバグの原因は特定しづらくなります。適宜テストを実施することは「急がば回れ」のようなものです。
先のif else構文では処理を 2分岐させましたが、それらを 3つ以上に分岐させることも可能です。次の形式で記述します。
if(条件A) {
// 条件Aが trueのとき実行される処理
} else if(条件B) {
// (条件Aが falseで、かつ)条件Bが trueのときに実行される処理
} else {
// 上記以外のとき(条件Aも条件Bも falseのとき)に実行される処理
}
先のプログラム(E102)では、マイナスの値(例えば -3)を入れても未成年と表示されてしまいます。本来はマイナスの年齢など存在しないから異常処理を加えることにします。そして、65歳以上の場合には高齢者と表示することにします。つまり 4分岐の処理となります。
これらをまとめた仕様を以下に定めてみます。
上記の仕様を満たしたプログラムは以下の通り。
E103/MySystem.java(ライブラリをそのまま利用します)
E103/E103.java
/**
* if ~ else if ~ else構文を確認します。
*/
public class E103 {
/**
* メインメソッド。
* @param args 引数
*/
public static void main(String[] args) {
int age;
age = MySystem.in.getInt("年齢を入力してください");
if(age < 0) {
System.out.println("異常な値が入力されました");
} else if(age < 20) {
System.out.println("未成年です");
} else if(age < 65) {
System.out.println("大人です");
} else {
System.out.println("高齢者です");
}
}
}
実行結果の例は以下の通り。
年齢を入力してください? -15 異常な値が入力されました
年齢を入力してください? 10 未成年です
年齢を入力してください? 25 大人です
年齢を入力してください? 80 高齢者です
今回のプログラムでもブラックボックステストのひとつである境界値チェックをしてみましょう。以下の 9つの値を入力して、プログラムにバグが潜んでいないか確認します。
もし 9パターンのいずれも正しい動作をしたならば、恐らくバグが潜んでいる可能性は少ないだろうと判断できます。
if文を習得するとプログラムの幅が広がります。ここでは以下の 3本のプログラムを作成してみます。
可能なら、仕様だけを見て自分でプログラムを考えてみると勉強になりますよ。その際には、テストしてみると良いと思います。
仕様は以下とします。
この仕様を満たすプログラムを以下に載せます。
E104/MySystem.java(ライブラリをそのまま利用します)
E104/E104.java
/**
* 丁半ばくちのプログラムです。
*/
public class E104 {
/**
* メインメソッド。
* @param args 引数
*/
public static void main(String[] args) {
int sai1, sai2;
System.out.println("[丁半ばくち]");
sai1 = 1 + (int)(Math.random() * 6.0);
sai2 = 1 + (int)(Math.random() * 6.0);
System.out.println("サイコロを振りました。");
int input = MySystem.in.getInt("丁か半か(1=丁、それ以外=半)");
// sumは、サイコロの目の合計を格納する
int sum = sai1 + sai2;
System.out.println("サイコロの目は " + sai1 + "と " + sai2 + "で、合計は " + sum + "。");
// isChouは、サイコロの目が偶数のとき trueとなる
boolean isChou = (sum % 2 == 0);
if(input == 1) {
if(isChou) {
System.out.println("大当たりー。");
} else {
System.out.println("外れ。");
}
} else {
if(isChou) {
System.out.println("外れ。");
} else {
System.out.println("大当たりー。");
}
}
}
}
実行結果の例は以下の通り。
[丁半ばくち] サイコロを振りました。 丁か半か(1=丁、それ以外=半)? 1 サイコロの目は 4と 6で、合計は 10。 大当たりー。
仕様は以下とします。
この仕様を満たすプログラムを以下に載せます。
E104/MySystem.java(ライブラリをそのまま利用します)
E104/E104.java
/**
* うるう年かどうかを判定するプログラムです。
*/
public class E105 {
/**
* メインメソッド。
* @param args 引数
*/
public static void main(String[] args) {
System.out.println("[うるう年判定]");
int year;
year = MySystem.in.getInt("西暦を入力してください");
if(year < 0) {
System.out.println("入力した値は異常です。");
} else {
// isLeapYearは、うるう年のとき true
boolean isLeapYear;
if(year % 400 == 0) {
isLeapYear = true;
} else if(year % 100 == 0) {
isLeapYear = false;
} else if(year % 4 == 0) {
isLeapYear = true;
} else {
isLeapYear = false;
}
if(isLeapYear) {
System.out.println("西暦 " + year + "年はうるう年です。");
} else {
System.out.println("西暦 " + year + "年はうるう年ではありません。");
}
}
}
}
実行結果の例は以下の通り。
[うるう年判定] 西暦を入力してください? 2012 西暦 2012年はうるう年です。
[うるう年判定] 西暦を入力してください? 1900 西暦 1900年はうるう年ではありません。
プログラムにバグが潜んでいないか、テストをしましょう。今回のプログラムでは以下の値をチェックすると良いでしょう。
これらすべてで正常動作したならば、バグが潜んでいる可能性が低いということになります。
仕様は以下とします。
この仕様を満たすプログラムを以下に載せます。
E106/MySystem.java(ライブラリをそのまま利用します)
E106/E106.java
/**
* 年齢計算のプログラムです。
*/
public class E106 {
/**
* メインメソッド。
* @param args 引数
*/
public static void main(String[] args) {
int bornYear, bornMonth, bornDay;
int searchYear, searchMonth, searchDay;
System.out.println("[年齢計算プログラム]");
bornYear = MySystem.in.getInt("生まれた年は");
bornMonth = MySystem.in.getInt("生まれた月は");
bornDay = MySystem.in.getInt("生まれた日は");
searchYear = MySystem.in.getInt("調べたい年は");
searchMonth = MySystem.in.getInt("調べたい月は");
searchDay = MySystem.in.getInt("調べたい日は");
int age;
if(searchMonth < bornMonth) {
// 調べたい月が誕生月よりも前の場合
age = searchYear - bornYear - 1;
} else if(searchMonth == bornMonth && searchDay < bornDay) {
// 調べたい月が誕生日と同じで、調べたい日が誕生日よりも前の場合
age = searchYear - bornYear - 1;
} else {
age = searchYear - bornYear;
}
if(age >= 0) {
System.out.println("そのときは " + age + "歳です。");
} else {
System.out.println("まだ生まれていません。");
}
}
}
実行結果の例は以下の通り。
[年齢計算プログラム] 生まれた年は? 2000 生まれた月は? 3 生まれた日は? 5 調べたい年は? 2013 調べたい月は? 2 調べたい日は? 28 そのときは 12歳です。
[年齢計算プログラム] 生まれた年は? 2000 生まれた月は? 3 生まれた日は? 5 調べたい年は? 2000 調べたい月は? 3 調べたい日は? 4 まだ生まれていません
年齢計算のアルゴリズムは上記のもの以外にも幾つか考えれます。プログラムを自作した場合には、いろいろな値を入力してチェックすることで、バグがないかどうか調べてみてください。例えば、2000/3/2生まれの人について調べるのであれば、以下の値でチェックしてみましょう。
これらすべてで正常動作したならば、バグが潜んでいる可能性が低いということになります。
以下のように boolean型変数に対して「== true」と記述をしたくなるときがあります。
if(isLeapYear == true) {
しかし、以下の記述で代用できます。
if(isLeapYear) {
同様に boolean型変数に対して「== false」と記述して比較をしたくなるときがあります。
if(isLeapYear == false) {
しかし、以下の記述で代用できます。
if(! isLeapYear) {
それぞれ後者のほうがシンプルだということもありますが、もうひとつの理由があります。boolean型以外の型の比較において、うっかり「==」を「=」と記述してしまった場合、ビルドエラーになるため、うっかりに気づくことができます。
if(a = 3) { // ビルドエラー
しかし boolean型で、うっかり「==」を「=」と記述してしまった場合、ビルドエラーにならないため、うっかりに気づきません。しかし比較では無く代入が行われ、おそらくそれはバグになります。なので、boolean型では == による比較はしないようにしてください。
if(isLeapYear = true) { // isLeapYeaarに trueが代入される上に、条件は常に成立する
// ここに書いた記述は常に実行される
}
if else構文において、条件を否定しても同じ処理をさせることができます。例えば、
if(a == 30) {
// 処理 A
} else {
// 処理 B
}
は、以下と同じ意味となります。
if(a != 30) {
// 処理 B
} else {
// 処理 A
}
2通りの記述のうち読みやすいほうを選択してソースコードを記述することで、バグの発生を抑えられるかもしれません。
ド・モルガンの法則とは以下の法則です。
「かつ」が「または」反転することに注意する必要があります。例えば以下の処理は、
if(a > 10 && a < 30) {
// 処理 A
} else {
// 処理 B
}
処理を反転させて以下のように書くことができます。
if(a <= 10 || a >= 30) {
// 処理 B
} else {
// 処理 A
}
&&が||に変わることに注意してください。うっかりするとバグの基になります。
else ifの部分は文法上、微妙な問題をはらんでいることもあって、プログラミング言語によって少しずつ異なっていたりします。幾つかのプログラミング言語の if文を見ていきましょう。
まず C言語。Javaと同じelse ifです。
if(a == 1) {
(処理1)
} else if(a == 2) {
(処理2)
} else {
(処理3)
}
PHP。elseifという予約語を使用します。
if($a == 1) {
(処理1)
} elseif($a == 2) {
(処理2)
} else {
(処理3)
}
Perl。elsifという予約語を使用します。
if($a == 1) {
(処理1)
} elsif($a == 2) {
(処理2)
} else {
(処理3)
}
Python。elifという予約語を使用します。
if a == 1: (処理1) elif a == 2: (処理2) else: (処理3)