プログラムの流れの繰り返し
While文
ある条件が成立するあいだ処理を繰り返すのは、do文だけでなくwhile文によっても実現できます。
while文
正の整数値を読み込んで、その値を0までカウントダウンする過程を表示するプログラムを作っていきましょう。
//CountDown1.java
//正の整数値を0までカウントダウン
import java.util.Scanner;
public class CountDown1 {
public static void main(String[] args){
Scanner stdIn = new Scanner(System.in);
System.out.println("カウントダウンスタート");
int x;
do{
System.out.print("正の整数値:");
x = stdIn.nextInt();
}while(x <= 0);
while(x >= 0){
System.out.println(x); //xの値を表示
x--; //xの値をデクリメント
}
stdIn.close();
}
}
このプログラムでは、xに読み込んだ値が0以下である限り、繰り返されdo文が終了したときには正の値になっています。
その後に、変数xに読み込んだ値を0までカウントダウンする過程を表示する部分に用いているのが、while文(while statement)です。while文は、式を評価した値がtrueである限り文を繰り返し実行します。そのため、流れは以下のようになります。
増分演算子と減分演算子
カウントダウンのために利用しているのが、変数の値を1つ減らす — です。
後置増分演算子と後置減分演算子
単項演算子である減分演算子 — は、オペランドの値をデクリメントする演算子です。したがって、while文は、xが0以上の間、以下に示すループ本体を繰り返すことになります。
- xの値を表示
- xの値をデクリメント
結果、xの値は0になるまで、カウントダウンしながら表示されます。
–演算子とは逆に、オペランドの値をインクリメントするのが、++演算子です。概略は以下に示しておきます。
x++ | xの値をインクリメントする(1つ増加する)。生成するのは増加前の値。 |
x– | xの値をデクリメントする(1つ減少する)。生成するのは減少前の値。 |
この2つの演算子の特徴を利用すると先ほどのプログラムを簡略化することができます。
//CountDown2.java
//正の整数値を0までカウントダウン
import java.util.Scanner;
public class CountDown2 {
public static void main(String[] args){
Scanner stdIn = new Scanner(System.in);
System.out.println("カウントダウンスタート");
int x;
do{
System.out.print("正の整数値:");
x = stdIn.nextInt();
}while(x <= 0);
while(x >= 0){
System.out.println(x); //xの値を表示してデクリメント
}
stdIn.close();
}
}
後置増分演算子(postfix increment operator)と後置減分演算子(postfix decrement operator)の後置という名前は、オペランドの後ろに演算子を適用することからきています。
前置増分演算子と前置減分演算子
演算子 ++ と — には、オペランドの前に演算子を適用する形の前置増分演算子(prefix increment operator)と前置減分演算子(prefix decrement operator)もあります。
++x | xの値をインクリメントする(1つ増加する)。生成するのは増加後の値。 |
–x | xの値をデクリメントする(1つ減少する)。生成するのは減少後の値。 |
※後置(前置)の増分演算子/減分演算子を適用した式を評価して得られるのは、インクリメント/デクリメントを行う前(後)の値です。
式の評価順序
2項演算子の左オペランドは、右オペランドよりも先に評価されることになります。検証しているのが以下のプログラムです。
//EvaluationOrder.java
//式の評価順序の確認
public class EvaluationOrder {
public static void main(String[] args){
int a = 3;
int x = (a++) * (3 + a);
System.out.println("a = " + a);
System.out.println("x = " + x);
}
}
注目するのは、左オペランド(a++)の評価が行われ、そこから(2 + a)の評価が行われ、最後に*による乗算が行われます。そのため、この式の演算は以下の手順で行われます。
- 式 a++ が評価される。評価によって得られる値は、インクリメント前の3です。評価が完了されると、インクリメントされて4となる。
- 式 3 + a が評価される。評価によって生成される値は7 です。a の値は変化しない。
- 乗算 3 * 7 によって21が生成され、その値がxに入れられる。
式の値の切り捨て
以前のwhile文を振り返ってみましょう。
while(x >= 0){
System.out.println(x); //xの値を表示
x--; //xの値をデクリメント
}
このプログラムでは、後置減分演算子で変数xをデクリメントしています。もし、変数xの値が5であれば、この式を評価した値はデクリメント前の5になります。ここで注意すべきなのは、式x–を評価した値を使っていないことです。演算を行った結果は無視することができます。
式を評価した値を切り捨てる文脈では、前置形式、後置形式のどちらの演算子を使っても同じ結果が得られます。
文字リテラル
while文と++演算子を組み合わせたプログラムを作ってみましょう。キーボードから読み込んだ値の分だけ、アスタリスク記号を描画しましょう。
//PutAsterisk1.java
//読み込んだ個数だけ*を表示
import java.util.Scanner;
public class PutAsterisk1 {
public static void main(String[] args){
Scanner stdIn = new Scanner(System.in);
System.out.print("何個*を表示しますか?:");
int n = stdIn.nextInt();
int i = 0;
while(i < n){
System.out.print('*');
i++;
}
System.out.println();
stdIn.close();
}
}
このプログラムで画面に出力しているのは、’*’です。このように単一の文字を単一引用符 ‘ で囲んだ式が文字リテラル(character literal)です。文字列リテラルとは別になります。
- 文字リテラル ‘*’ …単一の文字 * を表す。型はchar
- 文字列リテラル “*” …文字 * だけから構成される文字の並びを表す。型はString
while文は0で初期化された変数 i の値をインクリメントしながら、’*’ を表示します。n個目の表示が終わった後にインクリメント i の値はnと等しくなるため、while文による繰り返しが終了します。
このプログラムは、以下のようにも実現することが出来ます。
//PutAsterisk2.java
//読み込んだ個数だけ*を表示
import java.util.Scanner;
public class PutAsterisk2 {
public static void main(String[] args){
Scanner stdIn = new Scanner(System.in);
System.out.print("何個*を表示しますか?:");
int n = stdIn.nextInt();
int i = 1;
while(i <= n){
System.out.print('*');
i++;
}
System.out.println();
stdIn.close();
}
}
while文とdo文
上のプログラムを実行した際に、負の値や0を入力してみましょう。そうすると*は一個も表示されないです。例えば、-5と入力するとwhile文の制御式 i < n の評価結果がfalseとなるため、ループ本体は一回も実行されないです。これがdo文とは大きく異なる特徴です。
- do文…後判定繰り返し:ループ本体を実行した後に判定を行う。
- while文…前判定繰り返し:ループ本体を実行する前に判定を行う。
本来は、do文もwhile文もfor文もループ本体が単一の文であれば、わざわざブロックにする必要はありません。しかし、do文に限っては、ループ本体が単一であっても{ }を導入した方がプログラムが読みやすくなります。
複合代入演算子
以下に示すのは、読み込んだ整数値を反転して表示するプログラムになります。
//ReverseNo.java
//正の整数値を読み込んで逆順に表示
import java.util.Scanner;
public class ReverseNo {
public static void main(String[] args){
Scanner stdIn = new Scanner(System.in);
System.out.println("正の整数値を逆順に表示します。");
int x;
do{
System.out.print("正の整数値:");
x = stdIn.nextInt();
}while(x <= 0);
System.out.print("逆から読むと");
while(x > 0){
System.out.print(x % 10); //xの最下位桁を表示
x /= 10; //xを10で割る
}
System.out.println("です。");
stdIn.close();
}
}
数値の逆転
while文のループ本体で行うのは、以下の2つのことです。
- xの最下位桁の表示…xの最下位桁の値であるx%10を表示、例えばxが1254なら、表示するのは10で割った余りの4になります。
- xを10で割る…表示後に行うのはxを10で割ることです。
演算子/=は、左オペランドの値を右オペランドの値で割ります。これらの処理を繰り返してxの値が0になると、while文が終了します。
演算子*, / , % , + , – , << , >> , >>> , & , ^ , | に対しては、その直後に = を付けた演算子が用意されています。これらの演算子は、演算と代入の2つの働きをもつため、複合代入演算子(compound assignment operator)と呼ばれます。複合代入演算子には以下のメリットがあります。
- 行うべき演算を簡潔に表せる
- 左辺の変数名を書くのが一回で済む
- 左辺の評価が1回限りである
整数の和を求める
複合代入演算子を用いた別のプログラム例を見てみましょう。これは、1~nまでの和を求めるプログラムです。
//SumUp.java
//1からnまでの和を求める
import java.util.Scanner;
public class SumUp {
public static void main(String[] args){
Scanner stdIn = new Scanner(System.in);
System.out.println("1からnまでの和を求めます。");
int n;
do{
System.out.print("nの値:");
n = stdIn.nextInt();
}while(n <= 0);
int sum = 0; //合計
int i = 1;
while(i <= n){
sum += i; //sumにiを加える
i++; //iをインクリメント
}
System.out.println("1から"+ n + "までの和は" + sum + "です。");
stdIn.close();
}
和を求める部分のフローチャートは以下のようになります。
和を求める前準備として、和を格納する変数sumの値を0にして、繰り返しを制御するための変数 i の値を1にします。変数 i の値がn以下である間、繰り返し実行します。i の値が1つずつ増えるため、繰り返すのはn回となります。