[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

情報処理技法(Javaプログラミング)I 第2回

目次
索引

アルゴリズムとプログラム

アルゴリズムとは

前回の授業では、プログラムとは何かについて説明しました。 人間がプログラムを作ってコンピューターに与えると、コンピューターはそのプログラムに従って動くという内容でした。 プログラムには、もう一つの特徴付けがあります。 それは、「プログラムとはアルゴリズムの具体的な表現である。」です。 ここで、 アルゴリズム algorithm )とは、問題を解決するための手順のことです。

アルゴリズムの説明で、問題解決と言われても、ピンと来ないかもしれません。 例として、次の問題を考えます。

与えられた文字列が回文か否かを判定せよ。 ここで回文とは、「たけやぶやけた」のように、左から読んでも右から読んでも同じになる文字列のことである。

この問題は、次のような手順で解決できます。

  1. 与えられた文字列を s1 とする。
  2. s1 の左右を逆転した文字列を生成し、その文字列を s2 とする。
  3. s1 s2 を左から一文字ずつ比較する。
  4. もし途中で文字の違いが見つかったら"No"と答える。
  5. 最後まで文字の違いが見つからなかったら"Yes"と答える。

今、「いるかはまるい」という文字列が与えられたとします。 s1 は「いるかはまるい」に、 s2 は「いるまはかるい」になります。 左から一文字ずつ比較すると、3文字目で違いが見つかるので、"No"と答えます。 一方、「たけやぶやけた」の場合は、 s1 は「たけやぶやけた」に、 s2 も「たけやぶやけた」になります。 左から一文字ずつ比較しても違いは見つからないので、"Yes"と答えるというわけです。

この手順は、回文判定のアルゴリズムと言えます。 実際、この手順によって、回文を判定するという問題が解決します。

アルゴリズムは必ずしも一つとは限りません。 以下も回文判定のアルゴリズムです。

  1. 与えられた文字列を s1 とする。
  2. s1 の左から1番目の文字と右から1番目の文字、左から2番目の文字と右から2番目の文字、…と順番に比較する。
  3. もし途中で文字の違いが見つかったら"No"と答える。
  4. 文字列の中央になるまで違いが見つからなかったら"Yes"と答える。

アルゴリズムとプログラミング言語

アルゴリズムが分かっても、そのままではコンピューターは問題解決をしてくれません。 コンピューターが日本語を理解するわけでもないですし、どうやって「左右を逆転した文字列を生成」するのかも、はっきりしていません。 コンピューターに問題解決をしてもらうには、コンピューターに処理できるような形で、アルゴリズムを具体的に表現する必要があります。 この表現のための言語がプログラミング言語であり、表現自身がプログラムです。

Javaはプログラミング言語の一つです。 この授業の目的は、アルゴリズムをJavaのプログラムで表現し、コンピューターに問題解決をさせることであると言えます。 Javaを覚えることと、アルゴリズムを見つけることは、無関係ではありません。 Javaの知識があればこそ、Javaで表現しやすいアルゴリズムが構成できるからです。 授業では、Java、アルゴリズム、およびプログラミングを並行して身につけることにします。

なお、プログラミング言語はJavaの他にもたくさんあります。 現在よく使われているのは、C, C++, Javaです。 今までよく使われてきたのは、Fortran, Cobol, Lisp, Basic, Pascal, Prologといったところです。 プログラミング言語が変われば、解決しやすい問題の種類や、プログラムに対する考え方が変わります。 今はJavaで精一杯かもしれませんが、色々なプログラミング言語を経験し、問題や考え方に応じてプログラミング言語を選べるようになるのが理想的です。

プログラミング言語と似たものに、 スクリプト言語 script language )または スクリプティング言語 scripting language )というものがあります。 これは、特定の用途のために、簡単にプログラミングができるようにした言語です。 例えば、スクリプト言語JavaScriptを使えば、ウェブ・ブラウザーの中で動くプログラムが作成できます。 スクリプト言語はJavaScriptだけでなく、Perl, PHP, Pythonなど種類も多く、それぞれの得意分野で使われています。


コメントとインデント

コメント

プログラムの一部や全部の説明文がプログラムの中に書けると、プログラマーにとってプログラムが理解しやすくなります。 このような説明文を コメント comment )とよびます。 Javaのコンパイラーは、プログラムに記号 // が現れると、そこから行末までを無視します。 そこにコメントを書けば、プログラムに影響を与えずにプログラムの説明ができます。 次のプログラムはコメントの使用例です。

// k20x1001 東 京子 2021年10月1日
class ProgramTest {
    public static void main (String[] args) {
        System.out.println("OK"); // 動作確認
    }
}

また、記号 /* */ に囲まれた部分も無視されます。 この2つの記号は同じ行に書く必要はないので、たくさんの行を一度にコメント指定できます。 例えば、

class CommentTest {
    public static void main (String[] args) {
        System.out.println("OK");
        System.out.println("Good");
        System.out.println("Nice");
        System.out.println("Excellent");
    }
}

というプログラムは、どこも無視されませんが、

class CommentTest {
    public static void main (String[] args) {
        System.out.println("OK");
/*
        System.out.println("Good");
        System.out.println("Nice");
*/
        System.out.println("Excellent");
    }
}

とすると、GoodとNiceの行が無視されます。 ただし、このコメントは入れ子にできません。 OKからExcellentまで無視してもらおうとして、

class CommentTest {
    public static void main (String[] args) {
/*
        System.out.println("OK");
/*
        System.out.println("Good");
        System.out.println("Nice");
*/
        System.out.println("Excellent");
*/
    }
}

と書いても、最初の */ までがコメントだと見なされ、Excellentの行は無視されないのです。

注意: この授業の例題のプログラムには、一行一行に行番号のコメントを入れていますが、これは説明のためであり、自作のプログラムでは書く必要はありません。

インデント

プログラムの行頭のスペース(字下げ)は インデント indent )とよばれます。 Javaのコンパイラーは行頭のスペースを無視します。 インデントをうまく使うと、プログラムの構造を見やすくできます。

動作確認のプログラムでインデントを用いないと

class ProgramTest {
public static void main (String[] args) {
System.out.println("OK");
}
}

となります。 最後の2つの閉じブレース( } )がそれぞれどの開きブレース( { )に対応しているかが分かりにくいです。 インデントを用いずに長いプログラムを書くと、ブレースの対応が分からなくなり、プログラムの構造を見えなくしてしまいます。

この授業では、次の規則でインデントをします。

この規則に従ってインデントをすると、ブレースに囲まれた部分が下図のようなブロック(長方形)になるので、プログラムの構造が見やすくなります。

インデントのイメージ
インデントのイメージ

式と演算子(1)

前回の授業では、プログラムの例として、コンソールにOKと出力するプログラムを動かしました。 今回は、数(特に整数)を取り扱うことにします。

次のプログラムを動かすと、コンソールで以下のような出力が得られます。

ExpressionTest.java
/*  1*/ class ExpressionTest { // 式のテスト
/*  2*/     public static void main (String[] args) {
/*  3*/         System.out.println(100);
/*  4*/         System.out.println(250 + 100);
/*  5*/         System.out.println(250 - 100);
/*  6*/         System.out.println(250 * 100); // 掛け算
/*  7*/         System.out.println(250 / 100); // 割り算
/*  8*/         System.out.println(250 % 100); // 割った余り
/*  9*/         System.out.println(1000 + 250 * 3);
/* 10*/     }
/* 11*/ }
コンソール
100
350
150
25000
2
50
1750

Completed with exit code: 0

注意: このコンソールの出力は、オンライン・コンパイラーcodiva.ioを使った場合です。 JDKを使った場合は、javacコマンド( javac ExpressionTest.java )でコンパイル、javaコマンド( java ExpressionTest )で実行してください。

プログラムの最初の2行はそのまま書くものと考えてください。 ただし、 class の右はファイル名です。

プログラム中の 250 + 100 , 250 - 100 などは expression )とよばれるものです。 プログラムを動かすと、式が計算されているのが分かります。 ここで、記号 * が掛け算を表していることに注意してください。 また、記号 / は割り算を表していますが、この場合は2余り50という計算をして、2を計算結果としています。 余りを求めるには、 / の代わりに % を書きます。

+ , - , * , / , % などを 演算子 operator )とよびます。

整数(int型)の演算子
記号 演算
+ 足し算 19 + 3 ⇒ 22
- 引き算 19 - 3 ⇒ 16
* 掛け算 19 * 3 ⇒ 57
/ 割り算 19 / 3 ⇒ 6
% 割った余り 19 % 3 ⇒ 1

上記の最後の出力を見ると、式 1000 + 250 * 3 の計算は、はじめに掛け算をして、次に足し算をしていることが分かります。 これは、演算子 + より * のほうが優先順位が高いと決められていて、式は優先順位の高い演算子から順に計算されるからです。 もし、足し算を先に計算したければ、括弧を使って (1000 + 250) * 3 と書いてください。 この式の計算結果は3750となります。

演算子の優先順位について簡単に説明します。

  1. まず、括弧の中が最優先で計算されます。
  2. 次に演算子 * , / , および % が優先します。
  3. 最も優先順位が低いのは演算子 + - です。

ここで注意すべきことは、 + - は同じ優先順位であり、これらが続いているときは左から右に計算されることです。 例えば、式 5 - 3 + 1 (5 - 3) + 1 と見なされ、3という計算結果になります。 演算子 * , / , % についても同様です。

注意: 割り算で、0 で割ることはできません。 もし、そのような計算を行うと、エラーが発生してプログラムは途中で停止します。 コンパイルに成功しても、プログラムが動くとは限らないのです。

DivisionTest.java
/*  1*/ class DivisionTest { // 割り算のテスト
/*  2*/     public static void main (String[] args) {
/*  3*/         System.out.println(100 / 2);
/*  4*/         System.out.println(100 / 0); // エラー
/*  5*/         System.out.println(100 / 5);
/*  6*/     }
/*  7*/ }
コンソール
Exception in thread "main" java.lang.ArithmeticException: / by zero
        at DivisionTest.main(DivisionTest.java:4)
50

Completed with exit code: 1

注意: このコンソールの出力は、codiva.ioの場合です。 JDKの場合は、50を出力してからエラー・メッセージを出力します。


変数と代入文(1)

変数とは

プログラムでは、 変数 variable )というものが重要な役割を果たします。 変数とは、数などのデータが格納できる「入れ物」であると考えてください。 次の図は変数のイメージです。

変数のイメージ
変数のイメージ

変数には名前が付いています。 これを 変数名 variable name )とよびます。 この場合は x です。 また、変数にはデータがひとつ格納できます。 これを変数の value )とよびます。 この場合、変数 x の値は100です。

変数を使えるようにするには、変数の 宣言 declaration )という手続きが必要です。 変数の宣言を行うのが 宣言文 declaration statement )です。

変数にデータを格納することを、 代入 assignment )と言います。 代入を行うのが 代入文 assignment statement )です。

変数に格納されたデータを取り出すには、式の中で数の代わりに変数名を書きます。

以下は変数を使ったプログラムの例です。

VariableTest.java
/*  1*/ class VariableTest { // 変数のテスト
/*  2*/     public static void main (String[] args) {
/*  3*/         int x; // 宣言文
/*  4*/         x = 100; // 代入文
/*  5*/         System.out.println(x);
/*  6*/     }
/*  7*/ }
コンソール
100

Completed with exit code: 0

3行目では、変数の宣言を行って、変数を使えるようにしています。 変数名を x とし、ここに格納するデータの種類(型といいます)は整数(int型)だと言っています。

宣言文は、整数の場合は

int 変数名;

という形が基本です。

4行目は代入文です。 変数 x にデータ100を格納します。

代入文は、一般的に

変数名 = ;

という形をとります。 「 変数名 」に「 」の計算結果を格納するということです。 単なる数や変数も式の一種です。 従って、

変数名 = ;

ならば、「 変数名 」に「 」を格納しますし、

変数名1 = 変数名2;

ならば、「 変数名1 」に「 変数名2 」の値を格納します。

5行目は結果の出力です。 変数 x にはデータ100が格納されているので、

System.out.println(x);

の部分が

System.out.println(100);

と書いたように振舞います。

なお、変数名はプログラマーが決めます。 英単語などを使って、分かりやすい変数名にしてください。 数学の変数のように、1文字にする必要はありません。 変数名の条件は、アルファベットの大文字か小文字で始まり、アルファベットの大文字、小文字、数字、アンダースコア(_)を並べた文字列です。 ただし、次の文字列はJavaで特別な意味を持つ キーワード なので、変数名としては使えません。

Javaのキーワード
A abstract assert
B boolean break byte
C case catch char class const continue
D default do double
E else enum extends
F final finally float for
G goto
I if implements import instanceof int interface
L long
N native new
P package private protected public
R return
S short static strictfp super switch synchronized
T this throw throws transient try
V void volatile
W while

また、 null , true , false も変数名としては使えません。

Javaらしい変数名の付け方は、英単語を続けて書き、2番目以降の単語の先頭を大文字にするというものです。 例えば、ペットボトルの紅茶の値段を表す変数名としては、次のようなものが考えられます。

tea , bottledTea
値段を表しているのが明らかなとき。
price , unitPrice
ペットボトルの紅茶を表しているのが明らかなとき。
teaPrice , bottledTeaPrice
何を表しているかが明らかでないとき。
tea1 , tea2
数種類の紅茶があるとき。

次のプログラムはいくつかの変数を使う例です。

SomeVariables.java
/*  1*/ class SomeVariables { // いくつかの変数
/*  2*/     public static void main (String[] args) {
/*  3*/         int temp1, temp2, temp3;
/*  4*/         temp1 = 300;
/*  5*/         temp2 = 200;
/*  6*/         temp3 = 4 * temp1 + 3 * temp2;
/*  7*/         System.out.println(temp3);
/*  8*/         System.out.println(4 * 300 + 3 * 200);
/*  9*/     }
/* 10*/ }
コンソール
1800
1800

Completed with exit code: 0

3行目で、3つの変数 temp1 , temp2 , temp3 を宣言します(temporary=一時的な)。 これは、変数の宣言

int temp1;
int temp2;
int temp3;

をまとめたものです。 6行目で、変数 temp3 に式 4× temp1 +3× temp2 の値を代入します。 変数 temp1 の値は300で、 temp2 の値は200なので、4×300+3×200を計算して、この値1800が変数 temp3 に格納されます。

変数の初期化

前小節では次のようなプログラムを動かしました。

/*  1*/ class VariableTest {
/*  2*/     public static void main (String[] args) {
/*  3*/         int x;
/*  4*/         x = 100;
/*  5*/         System.out.println(x);
/*  6*/     }
/*  7*/ }

変数の宣言とデータの格納は一緒に行うことができます。 この操作を変数の 初期化 initialization )とよび、そのデータをその変数の 初期値 initial value )とよびます。 次のプログラムの3行目で、変数 x を100に初期化しています。

VariableTest2.java
/*  1*/ class VariableTest2 { // 変数のテスト2
/*  2*/     public static void main (String[] args) {
/*  3*/         int x = 100; // 初期化
/*  4*/         System.out.println(x);
/*  5*/     }
/*  6*/ }

次のように書くと、複数の変数が初期化できます。

int x = 100, y = 200;

次のように、変数の初期化と単なる宣言を混ぜることもできます。

int x = 100, y = 200, z;

変数の値の変更

今までの例では、変数に格納されたデータは特に変更されませんでした。 一般的には、変数に格納されたデータは何度も変更されます。

VariableTest3.java
/*  1*/ class VariableTest3 { // 変数のテスト3
/*  2*/     public static void main (String[] args) {
/*  3*/         int x = 100;
/*  4*/         x = 101; // 101を格納
/*  5*/         System.out.println(x);
/*  6*/     }
/*  7*/ }
コンソール
101

Completed with exit code: 0

このプログラムは、変数 x にデータ100を格納した後、さらにデータ101を格納するものです。 5行目で変数 x の値を取り出すと、101になります。 最初のデータ100は上書きされてしまいます。 このイメージは次の図のようになります。

変数の値の変更
変数の値の変更

変数の使用例

変数の使用例として、次の問題を考えます。

A子は店に行き、150円のペットボトルの紅茶を1本と120円の缶コーヒーを3本買いました。 A子はいくら支払わなければならないでしょうか。

この問題を解決するには、式 150+3×120 を計算するのが簡単です。 プログラムは次の通りです。

SomeDrinks.java
/*  1*/ class SomeDrinks { // いくつかの飲み物
/*  2*/     public static void main (String[] args) {
/*  3*/         System.out.println(150 + 3 * 120);
/*  4*/     }
/*  5*/ }
コンソール
510

Completed with exit code: 0

変数に計算結果を格納するアルゴリズムもあります。 まず、合計金額を表す変数 total を宣言しておきます。 次に、式を計算し、その結果を変数 total に格納します。 最後に、変数 total の値を取り出して出力します。

合計金額だけではなく、ペットボトルの紅茶と缶コーヒーの値段も変数に格納しておくと、より分かりやすくなります。 変数名は、それぞれ tea , coffee とします。 プログラムは次の通りです。

SomeDrinks2.java
/*  1*/ class SomeDrinks2 { // いくつかの飲み物2
/*  2*/     public static void main (String[] args) {
/*  3*/         int total, tea = 150, coffee = 120;
/*  4*/         total = tea + 3 * coffee;
/*  5*/         System.out.println(total);
/*  6*/     }
/*  7*/ }

演習2

次の問題を考えます。

B子はドラッグストアに行き、100円のティッシュを5箱と200円の石けんを2個と300円のタオルを1枚買いました。 B子はいくら支払わなければならないでしょうか。

この問題を解決するアルゴリズムを考え、プログラムを作成してください。 プログラムでは、変数を適切に利用してください。

Drugstore.java
class Drugstore {
    public static void main (String[] args) {



    }
}
コンソール
1200

Completed with exit code: 0

余力のある人は、同じような買い物を自分で考え、支払金額を求めるプログラムを作成してください。 ファイル名(英数字の列.java)は自分で決め、それをプログラムのclassの右に書きます。 プログラムでは、変数を適切に使って、何を買ったかが分かるようにしてください。


レポート課題

今日の演習2の答案(Javaプログラム)をメールで提出してください。 差出人は大学発行のメール・アドレス(学生番号@cis.twcu.ac.jp)とし、宛先はkonishi@cis.twcu.ac.jpとします。 メールの本文には、学生番号、氏名、科目名、授業日(10月1日)を明記してください。


参考文献


[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

2021年10月1日更新
小西 善二郎 <konishi@cis.twcu.ac.jp>
Copyright (C) 2021 Zenjiro Konishi. All rights reserved.