5.白黒はっきりさせよう
~条件分岐~

 ある条件によって処理を変えることはよくあることです。 「明日晴れたらピクニックに行こう!」と誘われたら、 「曇りだったら?天気雨だったら?降水量何ミリ以上なら中止?」と問い詰めてください。 嫌われること間違いなしです。でもコンピュータを誘うときは、きちんと条件を言ってあげてね。

ドキドキ合格?不合格?

 これを書いているときの、SunのJava認定試験の問題数は61問、正答率52%が合格ラインのようです。 ちなみに制限時間は2時間です。2時間といえば長いようですが、私は1回見直しできるかどうかでした。 さて問題数と正解数が与えられたとき、合格か不合格かを判定する処理を考えて見ましょう。

int all = 61, correct = 38;
double score = (double) correct / all * 100;

if (score >= 52) {
 System.out.println("合格");
} else {
 System.out.println("不合格");
}

 変数scoreの初期値に式が指定してあることに注目してください。 式の値が確定するならば、このような方法もOKです。
 条件で処理を分けるにはif文を使います。次の形になります。これで1文です。

if(条件)
 文1 …ひとつの文だけ指定できる
else
 文2 …ひとつの文だけ指定できる

 ()内の条件が成立するときは文1、不成立のときは文2を行います。
 上と照らし合わせると、scoreの値が52以上ならば合格、そうでなければ、つまり52未満ならば不合格と出力されることがわかります。

 おっと出力の文を囲む{}は何でしょう。
 実は条件成立時に行う処理が1文で記述できるという保証はないわけです。 ところが上の構文を見ると文1と文2はひとつの文しか指定できない形になっています。

 もし複数文指定したいときは{}で囲む必要があるのです。

 でも上の例はどちらも1文だけじゃないか、と思った方、確かにその通り。
 しかしながらもしかすると合格なら頭の上でくすだまを割ることにしよう、となるかもしれません。 後から何か追加したいときに、{ }をあらかじめ入れておくと変更が楽だし間違えも少なくてすみます。

 そこで上の構文はこう書き直しておきましょう。

 {}で囲まれた文の集まりをブロックといいます。 この書き方に慣れてしまいましょう。

if(条件) {
 文の集まり1
} else {
 文の集まり2
}

 大小比較の記号は算数と同じで<や>を使います。 でも≦や≧は半角文字にはないので、<=と>=で代用します。 必ず=は後にしてください。=<や=>ではありません。
 等しいかどうかは=ではなく==、等しくないかどうかは≠ではなく!=になります。

AかBかCかDか

 今度はもう少し複雑に、 正答率が80%以上ならばランクA、60%以上80%未満ならばランクB、40%以上60%未満ならばランクC、40%未満ならばランクDとします。 ”未満”という言葉は大丈夫ですね。 「18歳未満お断り」は18歳の誕生日からOKになります。一方「18歳以下お断り」は19歳の誕生日からOKになります。この1年の差は大きいですね。

if (score >= 80) {
 System.out.println("ランクA");
} else if (score >= 60) {
 System.out.println("ランクB");
} else if (score >= 40) {
 System.out.println("ランクC");
} else {
 System.out.println("ランクD");
}

 構文は次のようになります。

if(条件1) {
 文の集まり1
} else if(条件2) {
 文の集まり2
} else if(条件3) {
 文の集まり3
} else {
 文の集まり4
}

 まず条件1をチェックします。成立すれば文の集まり1、成立しなければ条件2のチェックに移ります。
 次に条件2をチェックします。成立すれば文の集まり2、成立しなければ条件3のチェックに移ります。
 次に条件3をチェックします。成立すれば文の集まり3、成立しなければ文の集まり4を行います。

 もちろん条件は3個までとは限りません。ずっと長くしてもいいのです。
 ということで、上の処理はscoreが80以上ならばランクA、80未満で60以上ならばランクB、...と問題文とあっていることを確かめてください。 条件の指定順にも注意が必要です。次の二つ、上はいいですが、下はランクBがいなくなってしまいます。

if (score < 40) {
 System.out.println("ランクD");
} else if (score < 60) {
 System.out.println("ランクC");
} else if (score < 80) {
 System.out.println("ランクB");
} else {  System.out.println("ランクA");
}


if (score >= 80) {
 System.out.println("ランクA");
} else if (score >= 40) {
 System.out.println("ランクC");
} else if (score >= 60) { // 通らない
 System.out.println("ランクB");
} else {
 System.out.println("ランクD");
}

 さて今まで条件と呼んでいたものは、結果がboolean型で求められます。そこでこんな書き方もできるのです。

boolean judge = (score >= 52);
if (judge) {
 System.out.println("合格");
} else {
 System.out.println("不合格");
}

 ついでにboolean型の値は、trueかfalseしかとりません。直接これらの値を代入することもできます。

boolean judge = true;

力士になれるかしら?

 力士になるには、25歳未満、身長173以上、体重75kg以上でなければならないようです。
(他にも経験や運動能力で条件が変わる場合もあるようです。それから女性はだめのようです。)

では次の人はこの条件をクリアするでしょうか。

20歳175cm60kg 体重が足りずだめ
25歳160cm80kg 年齢と身長がだめ
18歳180cm80kg OK!

 つまり年齢、身長、体重の三つの条件が全部クリアしないとだめなのです。
 このような場合の条件は「25歳未満 かつ 身長173cm以上 かつ 体重75kg以上」と「かつ」を入れて言い換えてみてください。 そしてその「かつ」を&&で表します。

if (age < 25 && height >= 173 && weight >= 75) {
 System.out.println("パス");
} else {
 System.out.println("だめ");
}

 この頃は遊園地でもシルバー料金があるようで、入場料が子供並だったりします。 ある所では、3歳以下と75歳以上がただ、それ以外は300円という料金設定だとしましょう。 この場合「3歳以下」と「75歳以上」をつなぐ言葉は「かつ」ではなく「または」になります。そしてその「または」は縦棒2本の||で表します。

if (age <= 3 || age>= 75) {
 fee = 0;
} else {
 fee = 300;
}

 「かつ」は前後の条件が両方成立して初めて全体としての条件が成立するのに対し、 「または」は前後どちらかの条件が成立すれば全体としての条件も成立します。

うるう年に挑戦

 西暦からうるう年を判定する方法を知っていますか?そう、4で割り切れる年はうるう年ですね。 でも100で割り切れる年はうるう年じゃないって知ってました?さらに400で割り切れる年はうるう年なんです。こんがらがりますね。

西暦2004年 4で割り切れて、100で割り切れないので、うるう年
西暦2100年 4で割り切れるが、100で割り切れるので、うるう年でない
西暦2000年 400で割り切れるので、うるう年

 難しい条件なので、まずうるう年になる場合を二つに分けてみましょう。

 「4で割り切れるけど、100では割り切れない」
 「400で割り切れる」

 このふたつは「または」で繋ぐことができます。

 「4で割り切れるけど、100では割り切れない」
      または
 「400で割り切れる」

 前半は「かつ」を使って書き換えられます。

  「4で割り切れる」 かつ 「100で割り切れない」

 ではプログラムにしてみましょう。

if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) {
 System.out.println("うるう年");
} else {
 System.out.println("うるう年でない");
}

 数学で集合のお勉強をしたときに、ベン図というのをやったのを覚えていますか? こんな場合ベン図を利用すると条件がはっきりするものです。集合の得意な方は試してみてはどうでしょう。 (ヒント:4で割り切れる数、100で割り切れる数、400で割り切れる数の3種類の集合を考えます。)

 条件に使用する演算子にも優先順位があります。大小比較の演算子は、&&や||より優先順位が高いので、次の括弧は不要です。

 (year % 4 == 0) && (year % 100 != 0)

 また計算のための演算子は、比較の演算子より優先順位が高いので、次の括弧は不要です。

 (year % 4) == 0

 さらに&&は||より優先順位が高いので次の括弧も不要です。

 (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)

ただし上では見易さのためにつけておきました。

こんなことされた日にゃ

 文字をいたずらするプログラムです。

char c;
// ここでcに何か文字が入る
if (c == '|') {
 c = '1';
} else if (c == 'o') {
 c = '0';
} else if (c == '\t') {
 c = ' ';
}

 これは悪質です。アルファベットの小文字のエルを数字のイチに、オーをゼロに、タブをスペースに置き換えています。 文字の定数はシングルクォートで1文字囲んでください。
 ここで条件はすべてcと何かが等しいかを調べています。このような場合は、switch文が用意されています。書き換えてみましょう。

switch (c) {
 case '|' :
  c = '1';
  break;
 case 'o' :
  c = '0';
  break;
 case '\t' :
  c = ' ';
  break;
 default :
  break;
}

 変数cの値が、caseのところに指定した値と上から順に比較されます。 等しいところで右側の文を実行します。ここは複数文あっても括弧でくくる必要はありません breakでswitch文の直後に抜け出します。もしcaseのところに一致するものがなければ、defaultの右の文を実行します。

 構文はこうです。

switch(式) {
 case 式1:文の集まり1
 case 式2:文の集まり2
   : 
 default:文の集まりn
}

 式の値は、byte、char、short、intのいずれかの型でなければなりません。
 defaultはなくてもいいです。でも入れておいて、落ちてきた不審なデータを拾い上げるのに役立つことがあります。
 またbreakがないと、次のcaseに処理が流れてしまうので、思わぬことになります。もちろんわざとやる場合は別。

switch (c) {
 case '|' :
  c = '1';
  // break;
 case 'o' :
  c = '0';
  break;
 case '\t' :
  c = ' ';
  break;
 default :
  break;
}

 上のようにひとつbreakをとってしまうと、cがエルの場合いったんはイチが代入されますが、 そのまま次のゼロの代入に流れ次のbreakで飛び出します。よってエルはゼロになってしまうというわけです。

 もうひとつここで見ておいてほしいのは、タブの'\t'です。これも文字の定数で特別エスケープシーケンスと呼んでいます。 次のようなものがあります。

\b バックスペース
\t タブ
\n 改行
\r 復帰
\" ダブルクォート
\' シングルクォート
\\ 円サイン