カテゴリー
Java

Java(Step4-4)

プログラムの流れの繰り返し

多重ループ

繰り返し文のループ本体を繰り返し文にすると、二重、三重の繰り返しを行うことができます。このような繰り返しを多重ループといいます。

九九の表

二重ループを用いた例として、九九の表を表示するプログラムを作りましょう。

//MultiTable.java
//九九の表を表示

public class MultiTable {
    public static void main(String[] args){
        for(int i = 1; i <= 9; i++){
            for(int j = 1; j <= 9; j++){
                if( i * j < 10)
                    System.out.print("  ");
                else
                    System.out.print(" ");
                System.out.print(i * j);
            }
            System.out.println();
        }
    }    
}
MultiTable.java実行結果

外側のfor文では、iの値を1から9までインクリメントします。それは、縦方向の繰り返しになります。その内側にあるfor文は、jの値を1から9までインクリメントします。これは、横方向の繰り返しになります。

したがって、この二重ループでは、以下のように処理が行われます。

  • i が 1 のとき: j を 1 ➡ 9 とインクリメントしながら、1 * j を表示。そして改行。
  • i が 2 のとき: j を 1 ➡ 9 とインクリメントしながら、2 * j を表示。そして改行。
  • i が 3 のとき: j を 1 ➡ 9 とインクリメントしながら、3 * j を表示。そして改行。
  • …中略…
  • i が 9 のとき: j を 1 ➡ 9 とインクリメントしながら、9 * j を表示。そして改行。

プログラムのif文は、数値間の余白調整です。以下のように出力を行います。

  • 表示する値が10未満(すなわち1桁)…数値の前にスペースを2個表示。
  • 表示する値が10以上(すなわち2桁)…数値の前にスペースを1個表示。

直角三角形の表示

二重ループを使えば、記号文字を並べて三角形や四角形などの図形を表示できます。以下が三角形を表示するプログラムになります。

//IsoscelesTriangle.java
//左下側が直角の直角三角形を表示
import java.util.Scanner;

public class IsoscelesTriangle {
    public static void main(String[] args){
        Scanner stdIn = new Scanner(System.in);

        System.out.println("左下直角の三角形を表示します。");
        System.out.print("段数は:");
        int n = stdIn.nextInt();

        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= i; j++)
                System.out.print('*');
            System.out.println();
        }
        stdIn.close();
    }    
}
IsoscelesTriangle.java実行結果

直角三角形の表示を行うフローチャートを以下に示します。

直角三角形を表示する二重ループのプログラムの流れ

実行例のように、nの値が7である場合を例にとって、どのように処理が行われるかを考えましょう。外側のfor文では、変数iの値を1からnすなわち7までインクリメントします。これは、三角形の各行に対応する縦方向の繰り返しです。その各行で実行される内側のfor文は、変数jの値を1からiまでインクリメントしながら表示を行います。これが、各行における横方向の繰り返しです。

break文とcontinue文

ここで、説明するのは、break文とcontinue文です。これらの文を利用すると、繰り返し文におけるプログラムの流れに変化を持たせることができます。

break文

以下に示すのは、読み込んだ整数の合計を表示するプログラムです。まず、最初に整数の個数を変数nに読み込みます。それから、for文によるn回の繰り返しの過程で、n個の整数を読み込んでいきながら加算を行います。ただし、読み込んだ値が0であれば入力は終了するようになっています。

//SumBreak1.java
//読み込んだ整数を加算
import java.util.Scanner;

public class SumBreak1 {
    public static void main(String[] args){
        Scanner stdIn = new Scanner(System.in);

        System.out.println("整数を加算します。");
        System.out.print("いくつ加算しますか?:");
        int n = stdIn.nextInt();

        int sum = 0;
        for(int i = 0; i < n; i++){
            System.out.print("整数0で終了:");
            int t = stdIn.nextInt();
            if(t == 0)break;   //for文から抜け出せる
            sum += t;
        }
        System.out.println("合計は" + sum + "です。");
        stdIn.close();
    }
}
SumBreak1.java実行結果

繰り返し文の中で使われるbreak文は、その繰り返しを強制的に中断・終了させます。そのため、変数nに読み込んだ値が0であれば、for文の実行は終了します。

break文を利用した別のプログラム例を以下に示します。合計が1000を超えない範囲で読み込みと加算を行う仕様に変更しています。

//SumBreak2.java
//読み込んだ整数を加算(1000以下まで)
import java.util.Scanner;

public class SumBreak2 {
    public static void main(String[] args){
        Scanner stdIn = new Scanner(System.in);

        System.out.println("整数を加算します。");
        System.out.print("いくつ加算しますか?:");
        int n = stdIn.nextInt();

        int sum = 0;
        for(int i = 0; i < n; i++){
            System.out.print("整数:");
            int t = stdIn.nextInt();
            if(sum + t > 1000){
                System.out.println("合計が1000を超えました。");
                System.out.println("最後の数値は無視されます。");
                break;
            }
            sum += t;
        }
        System.out.println("合計は" + sum + "です。");
        stdIn.close();
    }
}
SumBreak2.java実行結果

実行例では、3個の整数を読み込んでいます。3個目の392を加算すると、合計が1000を超えるため、読み込みを中断します。sumには最初に読み込んだ2個の合計が入っていることになります。

continue文

break文と対照的な構文を持つcontinue文(continue statement)です。continueが実行されると、ループ本体の残りの部分がすっ飛ばされて、プログラムの流れはループ本体の末尾に一気に飛びます。

continue文を利用したプログラム例を以下に示します。前のプログラムと同様に、読み込んだ整数を加算します。ただし、加算するのは0以上の値のみです。変数tに読み込んだ値が0未満であれば、「負の数は加算されません。」と表示したうえでcontinue文を実行します。そのため、加算を行う部分はスキップされます。

//SumContinue.java
//読み込んだ整数を加算(負の値は加算しない)
import java.util.Scanner;

public class SumContinue {
    public static void main(String[] args){
        Scanner stdIn = new Scanner(System.in);

        System.out.println("整数を加算します。");
        System.out.print("いくつ加算しますか?:");
        int n = stdIn.nextInt();

        int sum = 0;
        for(int i = 0; i < n; i++){
            System.out.print("整数:");
            int t = stdIn.nextInt();
            if(t < 0){
                System.out.println("負の数は加算されません。");
                continue;
            }
            sum += t;
        }
        System.out.println("合計は" + sum + "です。");
        stdIn.close();
    }
}
SumContinue.java実行結果

ラベル付きbreak文

ここまで、break文とcontinue文を一重ループに適用したプログラム例を説明しました。多重ループの実行中に、外側の繰り返しを一気に抜けたり、強制的に繰り返しを行いたい場合、ラベル付きの文を使う必要があります。以下はラベル付きbreak文になります。

//SumGroup1.java
//読み込んだ整数のグループを加算
import java.util.Scanner;

public class SumGroup1 {
    public static void main(String[] args){
        Scanner stdIn = new Scanner(System.in);

        System.out.println("整数を加算します。");
        int total = 0;

        Outer:
            for(int i = 1; i <= 10; i++){
                System.out.println("□第" + i + "グループ");
                int sum = 0;
            Inner:
                for(int j = 0; j < 5; j++){
                    System.out.print("整数:");
                    int t = stdIn.nextInt();
                    if(t == 99999)
                        break Outer;
                    else if(t == 88888)
                        break Inner;
                    sum += t;
                }
                System.out.println("小計は" + sum + "です。\n");
                total += sum;
            }
            System.out.println("\n合計は" + total + "です。\n");
    }    
}
SumGroup1.java実行結果

このプログラムは、5個の整数から構成されるグループの合計を求めます。グループは10個ですが、以下のように入力することで読み込みを中断できます。

  1. 99999を入力すると、全体の入力を終了する
  2. 88888を入力すると、現在入力中のグループの入力を終了する

プログラム全体は二重のfor文となっています。外側のfor文にはラベル Outer が付いて、内側のfor文には、ラベル Inner が付いています。

このように、ラベルが付いた文をラベル付き文(labeled statement)と呼びます。

プログラムの流れがラベル付きbreak文に差し掛かると、そのラベルをもった繰り返し文が終了します。そのため、本プログラムのbreak文は以下のように動きます。

  1. break文が実行されると、ラベル Outer: の付いた for 文の実行が中断される。
  2. break文が実行されると、ラベル Inner: の付いた for 文の実行が中断される。

ラベル付きcontinue文

もし、グループごとの小計を求める必要がなければ、より簡単に書き換えることができます。ラベル付きのcontinue文で書き換えた例が以下になります。

//SumGroup2.java
//読み込んだ整数のグループを加算
import java.util.Scanner;

public class SumGroup2 {
    public static void main(String[] args){
        Scanner stdIn = new Scanner(System.in);

        System.out.println("整数を加算します。");
        int total = 0;

        Outer:
            for(int i = 1; i <= 10; i++){
                System.out.println("□第" + i + "グループ");
            
                for(int j = 0; j < 5; j++){
                    System.out.print("整数:");
                    int t = stdIn.nextInt();
                    if(t == 99999)
                        break Outer;
                    else if(t == 88888)
                        continue Outer;
                    total += t;
                }
            }
            System.out.println("\n合計は" + total + "です。\n");
    }    
}
SumGroup2.java実行結果

printfメソッド

次に画面表示を行うprintfメソッドについて説明します。これを利用すると、出力時に桁数などの書式設定が可能となります。

printfメソッド

以下に示すのは、九九の表を出力するプログラムからの抜粋になります。

for(int i = 1; i <= 9; i++){
            for(int j = 1; j <= 9; j++){
                if( i * j < 10)
                    System.out.print("  ");
                else
                    System.out.print(" ");
                System.out.print(i * j);
            }
            System.out.println();
        }

基数や桁数などの書式を制御して画面への表示を行う System.out.printf というメソッドを利用すれば、プログラムは簡潔になります。書き換えたのが以下のプログラムになります。

//MultitablePrintf.java
//九九の表示(System.out.printf利用)

public class MultitablePrintf {
    public static void main(String[] args){
        for(int i = 1; i <= 9; i++){
            for(int j = 1; j <= 9; j++){
                System.out.printf("%3d", i * j);
            }
            System.out.println();
        }
    }
}
MultitablePrintf.java実行結果

「%3d」は、コンマに続く整数値を少なくとも3桁の幅で10進数表示してください。という書式指定のための書式文字列です。%は、書式指定の開始を表す文字で、dは10進数という意味のdecimalの頭文字になります。

3桁でなく、少なくとも3桁と説明したのは、理由があります。出力すべき数値が指定された桁数で収まらなければ、その数値の全部の桁が出力されるからです。これは、下のプログラムを実行するとわかると思います。

//PrintfWidth.java
//いろいろな桁数の整数を少なくとも3桁で表示

public class PrintfWidth {
    public static void main(String[] args){
        System.out.printf("%3d\n", 1);
        System.out.printf("%3d\n", 12);
        System.out.printf("%3d\n", 123);
        System.out.printf("%3d\n", 1234);
        System.out.printf("%3d\n", 12345);
    }
}
PrintfWidth.java実行結果

次に、以下のプログラムを実行してみてください。書式文字列以外の文字が、そのまま画面に表示されることがわかると思います。

//PrintfDecimal.java
//System.out.printfによる整数値の出力

public class PrintfDecimal {
    public static void main(String[] args){
        int x = 55;
        int y = 123;
        System.out.printf("x = %3d\n", x);
        System.out.printf("y = %3d\n", y);
    }    
}
PrintfDecimal.java実行結果

キーボードから読み込んだ整数値と実数値をprintfメソッドによって表示するプログラム例を以下に示します。

//DecimalFloat.java
//整数値と実数値を表示
import java.util.Scanner;

public class DecimalFloat {
    public static void main(String[] args){
        Scanner stdIn = new Scanner(System.in);

        System.out.print("整数x:");
        int x = stdIn.nextInt();

        System.out.print("整数y:");
        double y = stdIn.nextDouble();

        System.out.printf("x =%3d y =%6.2f\n", x, y);
        stdIn.close();
    }
}
DecimalFloat.java実行結果

このプログラムでは、2つの数値の書式化を1度に行っています。複数の式の値を出力する場合は、各式をコンマ文字 , で区切ります。2つの変数の表示は、以下のように行われます。

  • 整数x…少なくとも3桁の10進数で表示します。
  • 実数y…全体を少なくとも6桁で、小数点以下の部分を2桁で表示します。f は、浮動小数点という意味のfloating-pointの頭文字です。

文字%は、書式指定の先頭文字です。文字%そのものを出力したい場合は、%%と表記する必要があります。

%d と %f 以外にも、色々な指定ができます。基本的なものを利用したプログラム例を以下に示します。

//PrintfTester.java
//System.out.printfのテストプログラム

public class PrintfTester {
    public static void main(String[] args) {
        System.out.printf("%d\n", 12345); //10進数
        System.out.printf("%3d\n", 12345); //少なくとも3桁
        System.out.printf("%7d\n", 12345); //少なくとも7桁
        System.out.println();

        System.out.printf("%5d\n", 123); //少なくとも5桁
        System.out.printf("%05d\n", 123); //少なくとも5桁
        System.out.println();

        System.out.printf("%d\n", 12345); //10進数
        System.out.printf("%o\n", 12345); //8進数
        System.out.printf("%x\n", 12345); //16進数(小文字)
        System.out.printf("%x\n", 12345); //16進数(大文字)
        System.out.println();

        System.out.printf("%f\n", 123.45); //浮動小数点数
        System.out.printf("%15f\n", 123.45); //全体を15桁
        System.out.printf("%9.2f\n", 123.45); //全体を9桁で小数点以下を2桁
        System.out.println();

        System.out.printf("XYZ\n"); //文字列変換なし
        System.out.printf("%s\n", "ABCDE"); //文字列
        System.out.printf("%3s\n", "ABCDE"); //少なくとも3桁
        System.out.printf("%10s\n", "ABCDE"); //少なくとも10桁
        System.out.println();
    }    
}
PrintfTester.java実行結果
変換文字解説
%d10進数で出力
%o8進数で出力
%x16進数で出力(a ~ f は小文字)
%X16進数で出力(A ~ F は大文字)
%c文字として出力
%f小数点形式で出力
%s文字列で出力
書式文字列