カテゴリー
Java

Java(Step5-2)

基本型と演算

演算と型

整数を整数で割ると、商や剰余が整数として得られますが、実数で求める方法を説明します。

演算と型

2つの整数値を読み込んで、その平均値を表示するプログラムを作りましょう。

//Average1.java
//2つの整数値の平均値を実数で求める(間違い)
import java.util.Scanner;

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

        System.out.println("整数値xとyの平均値を求めます");
        System.out.print("xの値:"); int x = stdIn.nextInt();
        System.out.print("yの値:"); int y = stdIn.nextInt();

        double ave = (x + y) / 2;
        System.out.println("xとyの平均値は" + ave + "です。");
        stdIn.close();
    }
}
Average1.java実行結果

実行例を見ると分かりますが、実数を表すdouble型の変数aveに平均値を入れていますが、7と8の平均が7.5ではんく、7.0となっています。なぜなら、2で割る演算がint / intの結果がint型になり、小数点以下が切り捨てられてしまうためです。

演算中にintとdoubleが混在した場合は、結果が変わってきます。これらの時、2項数値昇格(binary numerical promotion)と呼ばれる型変換(type conversion)が行われます。int型がdouble型に格上げされて演算が行われます。それを確認したのが次のプログラムになります。

//Quotient.java
//2つの数値の商を求める

public class Quotient {
    public static void main(String[] args){
        System.out.println("15 / 2 =" + 15 / 2);
        System.out.println("15.0 / 2.0 =" + 15.0 / 2.0);
        System.out.println("15.0 / 2 =" + 15.0 / 2);
        System.out.println("15 / 2.0 =" + 15 / 2.0);
    }    
}
Quotient.java実行結果

double型は小数点以下の部分を格納する点でint型に比べ、余裕があります。具体的には、2項数値昇格における型変換は以下のようになっています。

  • 一方のオペランドがdouble型ならば、他方をdouble型に変換する
  • そうでないなら、一方のオペランドがfloat型ならば、他方をfloat型に変換する
  • そうでないなら、一方のオペランドがlong型ならば、他方をlong型に変換する
  • そうでなければ、両オペランドをint型に変換する

以上を踏まえ、平均値を実数値で求めるプログラムを改良すると以下のようになります。

//Average2.java
//2つの整数値の平均値を実数で求める(合計を2.0で割る)
import java.util.Scanner;

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

        System.out.println("整数値xとyの平均値を求めます");
        System.out.print("xの値:"); int x = stdIn.nextInt();
        System.out.print("yの値:"); int y = stdIn.nextInt();

        double ave = (x + y) / 2.0;
        System.out.println("xとyの平均値は" + ave + "です。");
        stdIn.close();
    }
}
Average2.java実行結果

キャスト演算子

普段の生活では、数字を割る時「2.0で割ろう」とは考えず、「2で割ろう」と考えます。2つの整数をはじめに実数に変換し、それを2で割ることで平均値を求めるようにしましょう。それが次のプログラムになります。

//Average3.java
//2つの整数値の平均値を実数で求める(キャスト演算子を利用する)
import java.util.Scanner;

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

        System.out.println("整数値xとyの平均値を求めます");
        System.out.print("xの値:"); int x = stdIn.nextInt();
        System.out.print("yの値:"); int y = stdIn.nextInt();

        double ave = (double)(x + y) / 2.0;
        System.out.println("xとyの平均値は" + ave + "です。");
        stdIn.close();
    }
}
Average3.java実行結果

平均値を求める部分が、以前のプログラムと異なります。演算子 / の左オペランドの式(double)(x + y)の形式を一般的に表すと次のようになります。

(型) 式

これは、式の値を型としての値に変換したものを生成するための式になります。この型変換をキャスト(cast)と呼びます。( ) は優先的に演算を行うための記号ではなく、キャスト演算子(cast operator)と呼ばれる演算子になります。

基本型の縮小変換

前のプログラムでは、int型の値をdouble型として取り出すためにキャスト演算を利用しました。次は逆の変換について考えてみましょう。

そこで、重要になるのがより小さい型への値の代入にはキャストが必須となることです。

次の変換がキャストが必須となる基本型の縮小変換(narrowing primitive conversion)となります。変換に伴い、数値の大きさの情報や精度が失われることがあります。

  • short から byte、char への変換
  • char から byte、short への変換
  • int から byte、short、char への変換
  • long から byte、short、char、int への変換
  • float から byte、short、char、int、long への変換
  • double から byte、short、char、int、long、float への変換
定数の代入・定数による初期化

基本型の縮小変換では、原則としてキャストが必要ですが、例外もあります。以下のようなケースです。

byte a = 0;
a = 5;
short b = 53;

代入の右辺の式や初期化子がbyte、short、char、int型の定数式で、代入先あるいは初期化先の変数の型がbyte、short、charであって、定数式の値が変数の型で表現できる場合は、基本型の縮小変換が自動で行われます。

基本型の拡大変換

縮小変換の逆が、基本型の拡大変換(widening primitive conversion)と呼びます。以下に示す種類があります。

  • byte から short、int、long、float、double への変換
  • short から int、long、float、double への変換
  • char から int、long、float、double への変換
  • int から long、float、double への変換
  • long から float、double への変換
  • float から double への変換

この変換は代入か初期化の際に自動的に行われます。基本型の拡大変換では、基本的に数値の大きさの情報が失われません。しかし、以下の変換では精度を失うことがあります。

  • int あるいは long の値から float への変換
  • long の値から double への変換

この場合の浮動小数点の変換結果は、最も近い値に丸められた整数値になります。例となるプログラムを以下に示します。

//IntegralToFloat.java
//整数型から浮動小数点型への変換(精度が失われる例)
public class IntegralToFloat {
    public static void main(String[] args){
        int a = 123456789;
        long b = 1234567890123456789L;

        System.out.println(" a = " + a);
        System.out.println("(float) a =" + (float)a);
        System.out.println(" b = " + b);
        System.out.println("(double) b =" + (double)b);
    }
}
IntegralToFloat.java実行結果

基本型の拡大変換と縮小変換

以下の変換は、基本型拡大変換と縮小変換の2段階で行われます。

  • byte から char への変換

まず、byteが基本型の拡大変換でintに変換され、その後、縮小変換によってintからchar型に変換されます。

繰り返しの制御

次のプログラムは、float型の変数xの値を0.0から1.0まで0.001ずつ増やしながら表示して、最後に合計を表示するプログラムになります。

//FloatSum1.java
//0.0から1.0まで0.001単位で増やして合計を表示(繰り返しをfloat制御)
public class FloatSum1 {
    public static void main(String[] args){
        float sum = 0.0F;

        for(float x = 0.0F; x <= 1.0F; x += 0.001F){
            System.out.println("x = " + x);
            sum += x;
        }
        System.out.println("sum =" + sum);
    }
}
FloatSum1.java実行結果

最後のxの値が1.0ではないことに気を付けましょう。浮動小数点数が、すべての桁の情報を失うことなく、表現できるとは限らないということです。そのため、1000個分の誤差がxに累積します。

繰り返しの制御を整数で行うように書き直したプログラムを以下に示します。

//FloatSum2.java
//0.0から1.0まで0.001単位で増やして合計を表示(繰り返しをint制御)

public class FloatSum2 {
    public static void main(String[] args){
        float sum = 0.0F;

        for(int i = 0; i <= 1000; i++){
            float x = (float)i / 1000;
            System.out.println("x = " + x);
            sum += x;
        }
        System.out.println("sum =" + sum);
    }
}
FloatSum2.java実行結果

for文では、変数iの値を0から1000までインクリメントします。繰り返しの度にiを1000で割った値をxとします。xが目的とする実数値をピッタリと表現できるわけではありません。毎回xの値を求め直すので、誤差は累積しません。

カテゴリー
Java

Java(Step5-1)

基本型と演算

基本型

これまでは、色々型の変数や定数を使用してきましたが、Javaでは基本型と参照型があります。

基本型

前までは、主にint、double、String型の変数などを使用していました。Javaで利用できる型を大きく区別すると、以下の図のようになります。

  • 数値型(numeric type)…整数を表す5種類の整数型と、実数を表す2種類の浮動小数点型に分かれます。
  • 論理型(boolean type)…論理値を表す論理型は、真と偽のいずれかの値を表現する型です。
型とビビット

以前、説明したように変数は型から作られます。例えば、

int x; //int型の変数

と宣言されたxは、int型になります。式には型があり、型と値が同じであれば、記憶域上での内部表現も同じものになります。内部表現は、0または1の値を持つデータ単位であるビットの集まりになります。

整数型

整数型(integral)は、有限範囲の連続した整数を表現する型です。以下に示す5種類があります。

char, byte, short, int, long

これらの型では、小数点以下をもつ実数を表せません。各型で表現できる数値の範囲とビット数をまとめたのが以下の図になります。

整数型で表現できる範囲とビット数
  • char型

文字を表すための型になります。図に示すように、非負の値しか表せないという点で、他の型と異なります。0と正の値を表現する符号なし整数型です。

  • byte型/short型/int型/long型

整数を表すための型です。負の値、0、正の値を表現する符号付き整数型です。各型の表現できる値が異なるのは、構成ビット数が異なるからです。

性質と用途
byte名前が示す通りに、1バイト(8ビット)の整数。1バイトのデータを表す際に利用します。
short短い整数です。小さな値しか使わないことが分かっていて、記憶域を節約したい時に使います。
int整数型の中で最も基本的な型です。通常はこの型を使います。
long長い整数です。int型では、表現できない大きな値に使用します。
整数型の性質と用途
整数リテラル

整数型の定数を表すのが、整数リテラル(integer literal)ですが、詳しく説明していきます。

整数リテラルには以下の6種類があります。

  • 10進数リテラル(int型/long型)
  • 8進数リテラル(int型/long型)
  • 16進数リテラル(int型/long型)
整数接尾語

整数リテラルは、基本的にはint型です。整数接尾語(integer type suffix)と呼ばれる1またはLを末尾に付けた整数リテラルの型はlong型となります。

最小値最大値単項 – 演算子のオペランドの最大値
int021474836472147483648
long09223372036854775807L9223372036854775808L
10進数リテラルで表現できる最小値と最大値
8進数リテラルと10進数リテラルで表現できる最小値と最大値
10進整数リテラル

これまで使ってきた10や55といった整数リテラルは、日常で使う10進数で表されており、10進整数リテラル(decimal integer literal)と呼ばれます。

8進整数リテラル

8進整数リテラル(octal integer literal)は、10進整数リテラルと区別がつくように、先頭に0をつけて2桁以上で表記します。以下の2つの整数リテラルは、同じように見えますが値はまったく別物になります。

  • 13…10進整数リテラル(10進数での13)
  • 013…8進整数リテラル(10進数でノ11)
16進整数リテラル

16進整数リテラル(hexadecimal integer literal)は、先頭に0xまたは、0Xを付けて表記します。A~Fは大文字でも小文字でもOKです。

  • 0xA…16進整数リテラル(10進数での10)
  • 0x13…16進整数リテラル(10進数での19)

各整数リテラルを10進数で表示するプログラムを以下に示すので、確認してみましょう。

//DecOctHexLiteral.java
//整数リテラル(10進数/8進数/16進数)

public class DecOctHexLiteral {
    public static void main(String[] args){
        int a = 13; //10進数の13
        int b = 013; //8進数の13
        int c = 0x13; //16進数の13

        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("c = " + c);
    }    
}
DecOctHexLiteral.java実行結果
整数の内部

値はビットの並びとして表現されます。ここでは、byte型、short型、int型、long型の表現について説明していきます。

符号ビット

整数型の値が、どのようにビットで表されるか示したのが下の図になります。

int型の整数値25と-25の内部
非負の値

非負の値は、その2進表現を各ビットに対応させたものとして表されます。上の図が示すように10進数の25は2進数で11001なので、上位ビットは0で埋め尽くされます。

負の値

負の値は、2の補数表現という方法で表現されます。例えば、正の数の全ビットを反転したものは、1の補数と呼ばれます。そして、1の補数に1を加えたものが、2の補数になります。以下がその図になります。

正の値から2の補数表現を用いた負の値への変換

浮動小数点型

小数点以下の部分を持つ実数を表すのが、浮動小数点型(floating-point type)です。以下の2種類があります。

float, double

そして、次のプログラムは、これらの型の変数に数値を入れて表示するプログラムです。

//FloatDouble.java
//float型とdouble型の精度が有限であることを体感

public class FloatDouble {
    public static void main(String[] args){
        float a = 123456789;
        double b = 1234567890123456789L;

        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }    
}
FloatDouble.java実行結果

実行結果から変数に入れた値が正確に表現されていないことが分かります。整数型が有限範囲の連続した整数を表現するとは異なり、浮動小数点型の表現範囲は、大きさと精度の両方からの制限を受けるためです。

形式表現範囲精度ビット数
floatIEEE754形式±3.40282347E+38~±1.40239846E-45約6~7桁32
doubleIEEE754形式±1.79769313486231507E+378~±4.94065645841246544E-324約15桁64
浮動小数点型の特性
浮動小数点リテラル

57.3のように実数を表す定数を浮動小数点リテラル(floating-point literal)と呼びます。型を指定するのが、浮動小数点接尾語(float type suffix)です。float型を指定するのがfとFであり、double型を指定するのがdとDです。指定しない場合はdouble型とみなされることになっています。

論理型

論理値を表す論理型(boolean型)は、以下の文脈で利用することになります。

  • if文の制御式(条件判定のための式)
  • do文・while文・for文の制御式(繰り返しを続けるかどうかの判定のための式)
  • 条件演算子? : の第1オペランド
論理値リテラル

論理型の値を表すfalseとtrueが論理値リテラルと呼ばれ、構文図で表すと以下のようになります。

論理値リテラルの構文図

関係演算子・等価演算子・論理否定演算子が論理型の値を生成することを検証するプログラムを作ってみましょう。

//BooleanTester.java
//関係演算子・等価演算子・論理否定演算子が生成する値を表示
import java.util.Scanner;

public class BooleanTester {
    public static void main(String[] args){
        Scanner stdIn = new Scanner(System.in);
        System.out.print("整数a:"); int a = stdIn.nextInt();
        System.out.print("整数b:"); int b = stdIn.nextInt();

        System.out.println("a < b = " + (a < b));
        System.out.println("a <= b =" + (a <= b));
        System.out.println("a > b =" + (a > b));
        System.out.println("a >= b =" + (a >= b));
        System.out.println("a == b =" + (a == b));
        System.out.println("a != b =" + (a != b));
        System.out.println("!(a==0) =" + !(a == 0));
        System.out.println("!(b==0) =" + !(b == 0));

        stdIn.close();
    }
}
BooleanTester.java実行結果

「文字列+数値」と「数値̟+文字列」の演算では、数値が文字列に変換された上で連結されます同様に、「文字列+論理値」と「論理値+文字列」の演算では、論理型の値が”true”もしくは”false”という文字列に変換された上で連結が行われます。

カテゴリー
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文字列で出力
書式文字列
カテゴリー
Java

Java(Step4-3)

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

for文

定型的な繰り返しの制御を、while文よりも簡潔に実現する for文について説明していきます。

for文

読み込んだ個数だけ * を表示するプログラムをwhile文ではなく、for文(for statement)と呼ばれる文によって書き換えたものを以下に示します。

//PutAsteriskFor.java
//読み込んだ個数だけ*を表示
import java.util.Scanner;

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

        System.out.print("何個*を表示しますか?:");
        int n = stdIn.nextInt();

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

プログラムはwhile文よりも短くなります。for文に慣れるとwhile文よりも直観的に分かりやすいものとなります。

for文とwhile文は相互に置き換えることができます。以下の図は、同等に書いた各文になります。

for文とwhile文

for文のプログラムの流れは以下のようになります。

  1. まず、前処理ともいうべきAが評価、実行されます。
  2. 継続条件であるBの制御式がtrueである限り、文が実行されます。
  3. 文の実行後は、後始末的な処理あるいは次の繰り返しのための準備としてCが評価、実行されます。

for文の各部に関する細かい規則などを以下に示します。

A for初期化部

Aには変数を置くことができます。ここで、宣言する変数はそのfor文の中でのみ使用できます。異なるfor文で同一名の変数を使用するには、各for文毎に宣言が必要です。

B 式(制御式)

B部も省略できます。省略した場合、繰り返しの継続判定は常にtrueとみなされます。この後で学習するbreak文やreturn文をループ本体中で実行しない限り、永遠に繰り返す無限ループになります。

C for 更新部

複数の式をコンマで区切っておくこともできます。何も行うことが無ければ、Cも省略可能です。

フローチャート

ここでは、フローチャートとその記号について解説します。

  • 流れ図の記号

問題の定義、分析、解法の図的表現である流れ図=フローチャート(flowchart)と、その記号は以下の規格で定義されています。

JIS X0121「情報処理用流れ図・プログラム網図・システム資源図記号」

  • プログラム流れ図(program flowchart)

プログラム流れ図は、以下に示す記号で構成されます。

実際に行う演算を示す記号、制御の流れを示す線記号、プログラム流れ図を理解し、かつ作成するために便宜を与える特殊記号。

  • データ(data)

媒体を指定しないデータを表します。

  • 処理(process)

任意の種類の処理機能を表します。例えば、情報の値、形、位置を変えるように定義された演算、もしくは演算群の実行、または、それに続くいくつかの流れの方向の1つを決定する演算もしくは演算群の実行を表します。

  • 定義済み処理(predefined process)

サブルーチンやモジュールなど、別の場所で定義された1つ以上の演算又は命令群からなる処理を表します。

  • 判断(decision)

1つの入り口と複数の択一的な出口を持ち、記号中に定義された条件の評価に従って、唯一の出口を選ぶ判断機能、またはスイッチ形の機能を表します。想定される評価結果は、経路を表す線の近くに書きます。

  • ループ端(loop limit)

2つの部分から構成され、ループの始まりと終わりを表します。記号の2つの部分には、同じ名前を用います。ループの始端記号または、ループの終端記号の中に初期化・増分・終了条件を表記します。

  • 線(line)

制御の流れを表します。流れの向きを明示する必要がある時は、矢先を付けなければなりません。

  • 端子(terminator)

外部環境への出口、または外部環境からの入り口を表します。例えばプログラムの流れの開始もしくは終了を表します。

この他に、並列処理や破線などの記号があります。

奇数の列挙

まずは、整数値を読み込んで、その整数値以下の正の奇数1,3,5,… を表示するプログラムを作りましょう。

//Odd.java
//読み込んだ整数値以下の奇数を表示
import java.util.Scanner;

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

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

        for(int i = 1; i <= n; i += 2){
            System.out.println(i);
        }
        stdIn.close();
    }    
}
Odd.java実行結果

for文の中で i += 2 で使っているのは、右オペランドの値を左オペランドに加える複合代入演算子になります。変数 i に 2 を繰り返しの度に増やしていきます。

約数の列挙

次に読み込んだ整数値のすべての約数を表示するプログラムを作りましょう。

//Measure.java
//読み込んだ整数値の約数を全て表示
import java.util.Scanner;

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

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

        for(int i = 1; i <= n; i++){
            if(n % i == 0)
                System.out.println(i);
        }
        stdIn.close();
    }    
}
Measure.java実行結果

まず、変数nに整数値を読み込みます。続くfor文では、変数 i の値を1からnまでインクリメントします。n を i で割った余剰が0ならば、i は n の約数であると判断できます。

複数変数の同時制御

これまでのプログラムのfor文は、1つの変数の値を元にして繰り返しを制御するものでした。for文では、複数の変数を同時に制御することも可能です。例が以下のようになります。

//For2var.java
//読み込んだ整数値と各値との差を表示
import java.util.Scanner;

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

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

        for(int i = 1, j = n-1; i <= n; i++, j--){
            System.out.println("数字" + i +  "との差は" + j);
        }
        stdIn.close();
    }    
}
For2var.java実行結果

プログラムの流れを確認しましょう。まず、最初に整数値 n に読み込みます。 2つの変数 i と j を宣言して、それぞれ 1 と n – 1 で初期化します。i の値をインクリメントする傍ら、j の値をデクリメントします。i の値は1ずつ増加し、j の値は1ずつ減少します。i <= nが成立しなくなると、繰り返しは終了します。

カテゴリー
Java

Java(Step4-2)

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

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();
    }    
}
CountDown1.java実行結果

このプログラムでは、xに読み込んだ値が0以下である限り、繰り返されdo文が終了したときには正の値になっています。

その後に、変数xに読み込んだ値を0までカウントダウンする過程を表示する部分に用いているのが、while文(while statement)です。while文は、式を評価した値がtrueである限り文を繰り返し実行します。そのため、流れは以下のようになります。

while文のフローチャート

増分演算子と減分演算子

カウントダウンのために利用しているのが、変数の値を1つ減らす — です。

後置増分演算子と後置減分演算子

単項演算子である減分演算子 — は、オペランドの値をデクリメントする演算子です。したがって、while文は、xが0以上の間、以下に示すループ本体を繰り返すことになります。

  1. xの値を表示
  2. 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();
    }    
}
CountDown2.java実行結果

後置増分演算子(postfix increment operator)と後置減分演算子(postfix decrement operator)の後置という名前は、オペランドの後ろに演算子を適用することからきています。

前置増分演算子と前置減分演算子

演算子 ++ と — には、オペランドの前に演算子を適用する形の前置増分演算子(prefix increment operator)と前置減分演算子(prefix decrement operator)もあります。

++xxの値をインクリメントする(1つ増加する)。生成するのは増加後の値。
–xxの値をデクリメントする(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);
    }    
}
EvaluationOrder.java実行結果

注目するのは、左オペランド(a++)の評価が行われ、そこから(2 + a)の評価が行われ、最後に*による乗算が行われます。そのため、この式の演算は以下の手順で行われます。

  1. 式 a++ が評価される。評価によって得られる値は、インクリメント前の3です。評価が完了されると、インクリメントされて4となる。
  2. 式 3 + a が評価される。評価によって生成される値は7 です。a の値は変化しない。
  3. 乗算 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();
    }
}
PutAsterisk1.java実行結果

このプログラムで画面に出力しているのは、’*’です。このように単一の文字を単一引用符 ‘ で囲んだ式が文字リテラル(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();
    }
}
PutAsterisk2.java実行結果

while文とdo文

上のプログラムを実行した際に、負の値や0を入力してみましょう。そうすると*は一個も表示されないです。例えば、-5と入力するとwhile文の制御式 i < n の評価結果がfalseとなるため、ループ本体は一回も実行されないです。これがdo文とは大きく異なる特徴です。

  • do文…後判定繰り返し:ループ本体を実行した後に判定を行う。
  • while文…前判定繰り返し:ループ本体を実行する前に判定を行う。
後後判定繰り返しの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();
    }    
}
ReverseNo.java実行結果

数値の逆転

while文のループ本体で行うのは、以下の2つのことです。

  1. xの最下位桁の表示…xの最下位桁の値であるx%10を表示、例えばxが1254なら、表示するのは10で割った余りの4になります。
  2. 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();
    }    
SumUp.java実行結果

和を求める部分のフローチャートは以下のようになります。

1からnまでの和を求めるフローチャート

和を求める前準備として、和を格納する変数sumの値を0にして、繰り返しを制御するための変数 i の値を1にします。変数 i の値がn以下である間、繰り返し実行します。i の値が1つずつ増えるため、繰り返すのはn回となります。

カテゴリー
Java

Java(Step4-1)

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

do文

プログラムで行う処理は一回だけ実行するのではなく、何度も繰り返して実行できます。そのための1つとしてdo文があります。

do文

前の記事では、入力された月の季節を表示するプログラムを作成しました。入力・表示は一回のみに限られています。何度でも入力できるように拡張していきましょう。それが、以下のコードです。

//Season2.java
//入力された月の季節を表示
import java.util.Scanner;

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

        do{
            System.out.print("季節を求めます。\n何月ですか:");
            int month = stdIn.nextInt();

            if(month >= 3 && month <= 5)
                System.out.println("その季節は春です。");
            else if(month >= 6 && month <= 8) 
                System.out.println("その季節は夏です。");
            else if(month >= 9 && month <= 11) 
                System.out.println("その季節は秋です。");
            else if(month >= 12 || month == 1 || month == 2 ) 
                System.out.println("その季節は冬です。");
        
            System.out.print("もう一度? 1…Yes/0…No :");
            retry = stdIn.nextInt();
        }while(retry==1);
        stdIn.close();
    }
}
Season2.java実行結果

mainメソッドの大部分がdoとwhileで囲まれています。この囲んでいる文をdo文(do statement)と呼びます。

doは、「実行せよ」という意味で、whileは「~のあいだ」という意味です。do文は、式を評価した値がtrueである限り、文を繰り返し実行します。このプログラムの流れは、図のようになります。

Season2.javaのdo文のフローチャート

繰り返しのことをループ(loop)と呼ぶので、do文が繰り返す文のことをループ本体(loop body)と呼ぶことにします。

一定範囲の値の読み込み

ジャンケンの手である「グー」「チョキ」「パー」を表示するプログラムを以前作りましたが、0、1、2以外の値を入力すると何も表示されません。do文を用いれば、この3つの値のみの入力しか受け付けないようにできます。そのように書き換えたのが以下のプログラムになります。

//FingerFlashing3.java
//読み込んだ値に応じてジャンケンを表示(0,1,2のみ)
import java.util.Scanner;

public class FingerFlashing3 {
    public static void main(String[] args){
        Scanner stdIn = new Scanner(System.in);
        int hand;
        do{
            System.out.print("何を出しますか?(0…グー、1…チョキ、2…パー):");
            hand = stdIn.nextInt();
        }while(hand < 0 || hand > 2);
        
        switch(hand){
        case 0:System.out.println("グー");break;
        case 1:System.out.println("チョキ");break;
        case 2:System.out.println("パー");break;
        }
        stdIn.close();
    }    
}
FingerFlashing3.java実行結果

このプログラムのdo文の制御式は、次のようになっています。

hand < 0 || hand > 2   //handが0~2の範囲外か

変数handの値が、不正な値(0より小さいかまたは2より大きい値)であれば、この式を評価した値はtrueとなります。そのため、handが0、1、2以外の値であれば、ループ本体であるブロックが繰り返し実行されます。

このdo文の流れを示したのが以下の図になります。

FingerFlashing3.javaのdo文のフローチャート
数あてゲーム

これまで習ってきた乱数、if文、do文を利用して数あてゲームを作ってみましょう。0~99の数字の中から数を当てるプログラムを以下に示します。

//NumGussing.java
//数当てゲーム(0-99)
import java.util.Random;
import java.util.Scanner;

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

        int num = rand.nextInt(100);

        System.out.println("数当てゲームスタート");
        System.out.println("0~99の値を当ててください。");
    
        int x;
        do{
            System.out.println("数を入力してください。:");
            x = stdIn.nextInt();

            if(x > num)
                System.out.println("もっと小さい数です。");
            else if(x < num)
                System.out.println("もっと大きい数です。");
        }while(x != num);
    System.out.println("正解です。");
        stdIn.close();
    }
}
NumGussing.java実行結果

プログラムのフローチャートを以下に示します。

NumGussing.javaのフローチャート
  1. 「数を入力してください」と数値の入力を促して、変数xに値を読み込みます。
  2. 読み込んだxの値がnoより大きければ、「もっと小さい数です」と表示し、xの値がnoより小さければ「もっと大きいです」と表示します。

それから、do文を繰り返すかどうかの判定を行います。判定のための制御式は、

x != no       //読み込んだxと当てるべき数noが等しくないか

です。したがって、読み込んだxの値が当てるべき数noと等しくない間do文が繰り返されることになります。数が当たったら、do文は終了し、「正解です」と表示してプログラムを終了します。

カテゴリー
Java

Java(Step3-3)

プログラムの流れの分岐

キーワード・識別子・演算子

ここでは、演算子を含めたプログラムの構成要素について説明していきます。

キーワード

if や else といった語句には、特別な意味が込められています。このような語句をキーワード(keyword)と呼ばれ、プログラム作成時に変数などの名前としては使えません。Javaのキーワードは、以下のようになっています。

abstractassertbooleanbreakbytecase
catchcharclassconstcontinuedefault
dodoubleelseenumextendsfinal
finallyfloatforgotoifimplements
importinstanceofintinterfacelongnative
newpackageprivateprotectedpublicreturn
shortstaticstrictfpsuperswitchsyncronized
thisthrowthrowstransienttryvoid
volatilewhile
キーワード一覧

正式なキーワードではないですが、true、false、nullも準じてあります。

区切り子

キーワードは、1種の単語のようなものです。その単語を区切るために使われる記号が区切り子(separator)です。以下の9つが使用されます。

[ ] ( ) { } , : .
区切り子の一覧

識別子

識別子(identifier)とは、変数、ラベル、メソッド、クラスなどに与えられる名前のことです。名前は自由に設定できますが、以下の規則に従う必要があります。

  • 識別子の1文字目は、次のいずれかでなければならない…いわゆる文字
  • 識別子の2文字目以降は、次のいずれかでなければならない…いわゆる文字、数字

数字が使えるのは2文字目からであることを覚えておきましょう。また、キーワードに加えて、true と false と null も識別子として使うことができません。

Javaでは、Unicodeという文字コード体系を使うため、”いわゆる文字”にはアルファベットだけでなく漢字文字なども含みます。

リテラル

整数リテラル、浮動小数点リテラル、文字列リテラルなども、プログラムを構成する要素の1つです。

演算子

数多くの演算子(operator)を学習しましたが、以下にJavaで利用できる全演算子をまとめています。

優先順位演算子形式名称結合規則
1[ ]x[y]インデックス演算子
1( )x(arg )メソッド呼び出し演算子
1.x.yメンバアクセス演算子
1++x++後置増分演算子
1x–後置減分演算子
2++++x前置増分演算子
2–x前置減分演算子
2++x単項 + 演算子
2-x単項 – 演算子
2!!x論理補数演算子
2~~xビット単位の補数演算子
3newnewnew 演算子
3( )( )キャスト演算子
4*x * y乗除演算子
4/x / y乗除演算子
4%x % y乗除演算子
5+x + y加減演算子
5x – y加減演算子
6<<x << yシフト演算子
6>>x >> yシフト演算子
6>>>x >>> yシフト演算子
7<x < y関係演算子
7>x > y関係演算子
7<=x <= y関係演算子
7>=x >= y関係演算子
7instanceofx instanceof yinstanceof演算子
8==x == y等価演算子
8!=x != y等価演算子
9&x & yビット論理積演算子
10^x ^ yビット排他的論理和演算子
11|x | yビット論理和演算子
12&&x && y論理積演算子
13||x || y論理和演算子
14? :x ? y : z条件演算子
15=x = y単純代入演算子
15*=x *= y複合代入演算子
15/=x /= y複合代入演算子
15%=x %= y複合代入演算子
15+=x += y複合代入演算子
15-=x -= y複合代入演算子
15<<=x <<= y複合代入演算子
15>>=x >>= y複合代入演算子
15>>>=x >>>=y複合代入演算子
15&=x &= y複合代入演算子
15^=x ^= y複合代入演算子
15|=x |= y複合代入演算子
全演算子の一覧
優先度

演算子の一覧表は、先頭側の方が優先度(precedence)が高くなるように表記しています。たとえば、乗除算を行う * と / が、加減算を行う + や – より優先度が高いのは、日常での計算法則と同じです。よって、

a + b * c

は、(a + b) * c ではなく、 a + (b * c)と解釈されます。

+演算子よりも優先度が低い演算子を文字列の連結時に使用する時は、( )が必要になることに注意しよう。

結合規則

同じ優先度の演算子が連続する時に左右どちらかの演算を先に行うかを示すのが、結合規則(associativity)です。例えば、2項演算子を○とした場合、式 a ○ b ○ c を

(a ○ b) ○ c 左結合

とみなすのが左結合の演算子であり、

a ○ (b ○ c)   右結合

とみなすのが右結合の演算子です。

代入式の評価

原則として式は、その値を評価することができるので、代入式であってもその評価を行うことができます。次のことは必ず覚えましょう。

  • 代入式を評価すると、代入後の左オペランドの型と値が得られる。

例えば、変数xがint型であれば、代入式 x = 5 を評価して得られるのは、代入後の左オペランドでxの型と値である「int型の5」になります。

以上でプログラムの分岐、構成要素については終わりです。次からは、プログラムの流れの繰り返しを説明していきます。

カテゴリー
Java

Java(Step3-2)

プログラムの流れの分岐

Switch文

if文は、ある条件の判定結果に応じて、プログラムの流れを2つに分岐していました。switch文を用いると、一度に複数の分岐ができます。

switch文

以下に示すのは、キーボードから入力された値に応じてジャンケンの手を表示するプログラミングです。

//FingerFlashing1.java
//読み込んだ値に応じてジャンケンを表示(if文)
import java.util.Scanner;

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

        System.out.print("何を出しますか?(0…グー、1…チョキ、2…パー):");
        int hand = stdIn.nextInt();

        if(hand == 0)
            System.out.println("グー");
        else if(hand == 1)
            System.out.println("チョキ");
        else if(hand == 2)
            System.out.println("パー");
        stdIn.close();
    }    
}
FingerFlashing1.java実行結果

このプログラムのif文は、handの値によって分岐しています。プログラミング作成時にはhandが何回もタイプするので、煩わしいです。

そこで、分岐を簡潔に表現するのがswitch文(switch statement)になります。switch文は、ある式を評価した値によってプログラムの流れを複数に分岐させる文であり、切り替えスイッチのようなものになります。

次に示すのは、switch文で上のプログラムを書き換えたものになります。

//FingerFlashing2.java
//読み込んだ値に応じてジャンケンを表示(switch文)
import java.util.Scanner;

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

        System.out.print("何を出しますか?(0…グー、1…チョキ、2…パー):");
        int hand = stdIn.nextInt();

        switch(hand){
        case 0:System.out.println("グー");break;
        case 1:System.out.println("チョキ");break;
        case 2:System.out.println("パー");break;
        }
        stdIn.close();
    }    
}
FingerFlashing2.java実行結果
ラベル

プログラムの流れがswitch文に差し掛かると、まず()内に書かれた制御式の評価が行われます。そして、その結果に応じてswitch文内のどこにプログラムの流れを移すか決定します。もし、制御式のhandの値が1であれば、プログラムの流れは

case 1:   //handが1のときの飛び先を表すラベル

と書かれた目印へと一気に移ります。プログラムの飛び先を示す目印がラベル(label)です。

プログラムの流れがラベルに飛んだあとは、その後ろに置かれた文が順次実行されます。この場合、handが1であれば、まず以下の文が実行されます。

System.out.println("チョキ");

これで、画面に”チョキ”と表示されます。

break文

プログラムの流れが、break文(break statement)と呼ばれる、

break;

に行き着くと、switch文の実行は終了することになります。breakとは「破る」意味の語句で、break文が実行されると、プログラムの流れはswitch文を突き破って抜け出ます。

より、switch文における、ラベルとbreak文の働きの理解を深めるために以下のコードをチェックしましょう。

//SwitchBreak.java
//switch文とbreak文の復習
import java.util.Scanner;

public class SwitchBreak {
    public static void main(String[] args){
        Scanner stdIn = new Scanner(System.in);
        System.out.print("整数を入力してください:");
        int n = stdIn.nextInt();

        switch(n){
            case 0:System.out.print("A");
                   System.out.print("B");
                   break;
            case 2:System.out.print("C");
            case 5:System.out.print("D");
                   break;
            case 6:
            case 7:System.out.print("E");
                   break;
            default:System.out.print("F");
                    break;
        }
        System.out.println();
        stdIn.close();
    }
}
SwitchBreak.java実行結果
defaultラベル

今回のプログラムには、

default:    //どのラベルとも一致しないときの飛び先を表すラベル

というラベルがあります。分岐のための制御式を評価した値が、どのcaseとも一致しないときは、プログラムの流れはこのラベルに飛ぶことになります。

選択文

if文とswitch文は、プログラムの流れを分岐させるという点で共通しています。これらをまとめて選択文(selection statement)と呼びます。

単一の式の値によるプログラムの流れの分岐は、if文ではなくswitch文によって実現したほうがよい場合が多いので、分岐処理をする際は読み手に分かりやすい処理を検討するようにしましょう。

カテゴリー
Java

Java(Step3-1-2)

プログラムの流れと分岐

if文

論理積演算子と論理和演算子

整数値を読み込んで、それが0なのか、1桁かそれ以上かを判断して表示するプログラムを作りましょう。

//DigitsNo1.java
//読み込んだ整数値の桁数判別(0、一桁、二桁以上)
import java.util.Scanner;

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

        System.out.print("整数値:");
        int n = stdIn.nextInt();
        if(n == 0)
            System.out.println("その値は0です。");
        else if(n >= -9 && n <= 9)
            System.out.println("一桁です。");
        else
            System.out.println("二桁以上です。");
        stdIn.close();
    }
}
DigitsNo1.java実行結果
論理積演算子 &&

このプログラムで利用している && 演算子は、論理積の演算を行う論理積演算子(logical and operator)です。日本語でいえば、「AかつB」だと考えれば分かりやすいです。

論理和演算子Ⅱ

もう1つの論理和を求めるのが論理和演算子(logical or operator)と呼ばれる || 演算子になります。これは、日本語でいう「AまたはB」に近い形です。この演算子を利用したプログラム以下になります。

//DigitsNo2.java
//読み込んだ整数値の桁数判別(2桁以上かどうか)
import java.util.Scanner;

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

        System.out.print("整数値:");
        int n = stdIn.nextInt();
        if(n <= -10 || n >= 10)
            System.out.println("2桁以上です。");
        else if(n >= -9 && n <= 9)
            System.out.println("2桁未満です。");
        stdIn.close();
    }
}
DigitsNo2.java実行結果
x && yxとyの両方ともtrueであればtrueを、そうでなければ、falseを生成します。
x || yxとyの一方でもtrueであればtrueを、そうでなければ、falseを生成します。
論理積演算子と論理和演算子

季節の判定

上の演算子2つを利用して季節(月)を判定するプログラムを作りましょう。

//Season.java
//読み込んだ月の季節を表示
import java.util.Scanner;

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

        System.out.print("季節を求めます。\n何月ですか:");
        int month = stdIn.nextInt();
        if(month >= 3 && month <= 5)
            System.out.println("その季節は春です。");
        else if(month >= 6 && month <= 8) 
            System.out.println("その季節は夏です。");
        else if(month >= 9 && month <= 11) 
            System.out.println("その季節は秋です。");
        else if(month >= 12 || month == 1 || month == 2 ) 
            System.out.println("その季節は冬です。");
        stdIn.close();
    }
Season.java実行結果
春・夏・秋の判定

&&を用いて、以下の要領で判定します。

  • monthが3以上かつmonthが5以下…春
  • monthが6以上かつmonthが8以下…夏
  • monthが9以上かつmonthが11以下…秋
冬の判定

冬の判定を用いるには、||を利用する必要があります。以下の要領で判定しています。

  • monthが12か1か2…冬
短絡評価

季節が春の際には以下のように記述しました。

month >= 3 && month <= 5

この判定の際に左オペランドのmonth >= 3がfalseとなった場合、右のオペランドは実行しなくてもfalseとなることが分かります。このように論理演算の式全体の評価結果が、左オペランドの評価結果のみで明確になる場合に、右オペランドが評価されないことを、短絡評価(short circuit evaluation)と呼びます。

条件演算子

2つの値を読み込んで小さい方の値を表示するプログラムを以下に示します。

//Min2.java
//読み込んだ2つの整数値の小さい方の値を表示(if文)
import java.util.Scanner;

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

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

        int min; //小さい方の値
        if(a < b)
            min = a;
        else  
            min = b;
        System.out.println("小さい値は" + min + "です。");
        stdIn.close();
    }    
}
Min2.java実行結果

変数a、bに読み込んだ値を比較して、aの方がbより小さければ変数minにaを代入し、そうでなければ変数minにbを代入します。その結果、if文の終了時に変数minには小さい値が入ることになります。

条件演算子

上のプログラムは、if文を用いずに条件演算子(conditional operator)で実現できます。

//Min2Cond.java
//読み込んだ2つの整数値の小さい方の値を表示(条件演算子)
import java.util.Scanner;

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

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

        int min = a < b ? a : b; //小さい方の値 
        System.out.println("小さい値は" + min + "です。");
        stdIn.close();
    }    
}
Min2Cond.java実行結果

条件式は、if文を凝縮したようなもので、Javaのプログラミングで好んで使われます。

x ? y : zxがtrueであればyを評価した値を、そうでなければzを評価した値を生成します。
条件演算子

三値の最大値

以下に示すのは、3つの変数a,b,cに整数値を読み込んで、その最大値を求めて表示するプログラムになります。

//Max3.java
//3つの整数値の最大値を求める
import java.util.Scanner;

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

        System.out.print("整数a:");
        int a = stdIn.nextInt();
        System.out.print("整数b:");
        int b = stdIn.nextInt();
        System.out.print("整数c:");
        int c = stdIn.nextInt();

        int max = a; //小さい方の値
        if(b > max)max = b;
        if(c > max)max = c; 
        System.out.println("最大値は" + max + "です。");
        stdIn.close();
    }   
}
Max3.java実行結果

三値の最大値を求める手段は、以下のようになっています。

  1. maxをaの値で初期化する
  2. bの値がmaxよりも大きければ、maxにbの値を代入する。
  3. cの値がmaxよりも大きければ、maxにcの値を代入する。

このように処理の流れを定義した規則をアルゴリズム(algorithm)と呼びます。

ブロック

以下に2つの整数値を読み込んで、小さい方の値と大きい方の値の両方を求めることにしたプログラムを示します。

//MinMax.java
//2つの整数値の大小比較表示
import java.util.Scanner;

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

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

        int min, max; 
        if(a < b){
            min = a;
            max = b;
        }else{
            min = b;
            max = a;
        }
        
        System.out.println("小さい値は" + min + "です。");
        System.out.println("大きい値は" + max + "です。");
        stdIn.close();
    }   
}
MinMax.java実行結果

このプログラムのif文は、aがbより小さければ、

{
min = a; max = b;
}

を実行し、そうでなければ次を実行します。

{
min = b; max = a;
}

いずれも、{ }で囲まれたており、このように囲んだものをブロック(block)と呼びます。

二値のソート

以下に示すのは、2つの変数a,bに整数値を読み込んで、昇順(小さい順)にソートする(sort:並べ替える)プログラムです。

//Sort2.java
//2つの変数を昇順にソート
import java.util.Scanner;

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

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

        if(a > b){
            int t = a;
            a = b;
            b = t;
        }
        System.out.println("小さい値から順に" + a + "、" + b + "です。");
        stdIn.close();
    }   
}
Sort2.java実行結果

ソートの手順は以下のようになっています。

  • aの値がbより大きい時…aとbの値を交換する
  • そうでないとき…何もしない(そのまま)
二値の交換

ブロック内で行っている手順は以下のようになります。

  1. aの値をtに保存しておく。
  2. bの値をaに代入する。
  3. tに保存しておいた最初のaの値をbに代入する。

この3つのステップで交換が完了します。

カテゴリー
Java

Java(Step3-1-1)

プログラムの流れと分岐

if文

ある条件が成立するかどうかによって、行うべき処理を決定するのがif文になります。

if – then 文

キーボードから数値を読み込んで、その値が0より大きければ、「その値は正です。」と表示するプログラムを作りましょう。

//Positive.java
//読み込んだ整数値の符号判定
import java.util.Scanner;

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

        System.out.print("整数値:");
        int n = stdIn.nextInt();
        if(n > 0)
        System.out.println("その値は正です。");
//n > 0がtrueのときに実行される
        stdIn.close();
    }
}
Positive.java実行結果

変数nに読み込んだ値を判定する部分が、if 文(if statement)と呼ばれ、その構文は、以下のようになっています。

if (式) 文

これは、if文の一種である if – then 文です。先頭のifは、「もしも~」という意味で、の値を調べて、その条件に一致する時(真)となったときにのみが実行されます。これからの説明では、条件判定のための制御式と呼ぶことにします。

if文の制御式n > 0で利用している > は、左オペランドが右オペランドより大きければ true(真)、そうでなければ false(偽)を生成する演算子になります。この2つは理論値リテラル(boolean literal)と呼ばれる論理(boolean)型のリテラルになります。

関係演算子

演算子 > のように、オペランドの大小関係を判定する演算子を、関係演算子(relational operator)と呼びます。関係演算子には、以下の4種類があります。

x < yxがyより小さければ true、そうでなければ false を生成
x > yxがyより大きければ true、そうでなければ false を生成
x <= yxがyより小さいか等しければ true、そうでなければ false を生成
x >= yxがyより大きいか等しければ true、そうでなければ false を生成
関係演算子

if – then – else文

前のプログラムでは、正でない値を読み込むと何も表示しません。正でない場合には、「その値は0か負です。」と表示するように変更しましょう。

//PositiveNot.java
//読み込んだ整数値の符号判定
import java.util.Scanner;

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

        System.out.print("整数値:");
        int n = stdIn.nextInt();
        if(n > 0)
            System.out.println("その値は正です。");
        else
            System.out.println("その値は0か負です。");
        stdIn.close();
    }
}
PositiveNot.java実行結果

このプログラミングのif文は、以下の構文をもちます。

if (式) 文 else 文

もちろんelseは「~でなければ」という意味を持ちます。制御式の値がtrueならば先頭側のを実行し、falseであれば、末尾側のを実行します。そのため、nが正であるかで異なる処理が実行されます。

等価演算子

キーボードから読み込んだ2つの整数値が等しいかどうか判定して表示するプログラムを作りましょう。

//Equal.java
//読み込んだ2つの整数値が等しいか判別
import java.util.Scanner;

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

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

        if(a == b)
            System.out.println("2つの値は等しいです。");
        else
            System.out.println("2つの値は等しくありません。");
        stdIn.close();
    }
}
Equal.java実行結果

変数aと変数bに値を読み込んで、それらの値の等価性を判断します。if文の制御式で利用している、==は、左右のオペランドが等しいかどうかを判断します。これを等価演算子(equality operator)と呼びます。また、等しくないかどうか判断する!=もあります。

x == yxとyが等しければtrueを、そうでなければfalseを生成
x != yxとyが等し毛羽trueを、そうでなければfalseを生成
等価演算子

論理補数演算子

以下に示しているのは、キーボードから読み込んだ値が0であるかどうかを判定して表示するプログラムになります。

//Zero.java
//読み込んだ整数値が0か判別
import java.util.Scanner;

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

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

        if(!(n != 0))
            System.out.println("その値は0です。");
        else
            System.out.println("その値は0ではありません。");
        stdIn.close();
    }    
}
Zero.java実行結果

単項演算子!は、論理補数演算子(logical complement operator)です。オペランドの値がfalseであれば、trueを生成し、trueであれば、falseを生成します。

!xxがfalseであれば、trueを、trueであれば、falseを生成
論理補数演算子

入れ子となったif文

以下は、キーボードから読み込んだ整数値の符号を判定して表示するプログラムです。

//Sign.java
//読み込んだ整数値の符号(正/負/0)を判別
import java.util.Scanner;

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

        System.out.print("整数値:");
        int n = stdIn.nextInt();
        if(n > 0)
            System.out.println("その値は正です。");
        else if(n < 0)
            System.out.println("その値は負です。");
        else
            System.out.println("その値は0です。");
        stdIn.close();
    }
}
Sign.java実行結果

このプログラムには、else if… とありますが、そのような構文が特別に用意されているわけではないです。elseが制御する文は、当然if文でもかまいません。

入れ子のif文を利用した別のプログラムの例を以下に示します。読み込んだ整数値の符号が正であれば、偶数/奇数のいずれかであるかを表示します。

//EvenOdd.java
//読み込んだ値が偶数か奇数か判別
import java.util.Scanner;

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

        System.out.print("整数値:");
        int n = stdIn.nextInt();
        if(n > 0)
            if(n % 2 == 0)
                System.out.println("その値は偶数です。");
            else
            System.out.println("その値は奇数です。");
        else
            System.out.println("正の値ではありません。");
        stdIn.close();
    }
}
Sign.java実行結果

式と評価

これまでに、(expression)という用語を何度か使いましたが、式は以下のものの総称になります。

  • 変数
  • リテラル
  • 変数やリテラルを演算子で結合したもの

以下の式を例に考えましょう。

abc + 32

変数abc、整数リテラル32、それらを+演算子で結んだ abc + 32 のいずれも式になります。○○演算子によって結合された式のことを、○○式と呼びます。

評価

式には、基本的にがあります。その値は、プログラム実行時に調べられます。式の値を調べることを評価(evaluation)といいます。

評価のイメージを具体的にしたのが次の図です。

式と評価

ここで、変数abcは、int型で値が50であるとします。abc、100、abc + 100はいずれも式です。変数abcの値が100なので、それぞれの式を評価した値は50、100、150になります。