結構長いプログラムをエラーなしで書くというのは大変な作業です。 ここでは、全然 (ほとんど) エラーが出ないようなプログラムを書く 技法を学びましょう。
プログラミングの例題として、「ドイツ語ドリル」を作ります。「ドリル」って いうのは僕が小学生の頃、宿題でやらされた問題集のようなもので、 「算数ドリル」とか「漢字ドリル」というようなものがありました。(今大学生の 君たちが小学生の頃こういうものをやったかどうかは知りません。)
それでこれから作るプログラムはドイツ語の単語の学習のための「ドリル」 みたいなものです。具体的には、
"画面にドイツ語の単語を表示して、ユーザにその日本語の意味を入力して もらう。もし正解ならば、「正解です。」と表示して、誤りならば、 正解を表示する。これを、数個の単語について繰り返す。"
というようなものです。仕様通りのものをいきなり全部書くのは大変です。 仕様通りでなくても良いので、とりあえず動くものを書きましょう。 例えば、
public class Drill { public static void main(String[] args) { System.out.println("Frau ?"); } }
のようなものです。(ちなみに Frau というのは、ドイツ語で「女性」とか 「女」という意味です。) これをコンパイルして実行すると、
banana:~/java% java Drill Frau ?
となるはずです。
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1);という命令文で reader 変数を用意して,
String line = reader.readLine();という命令文で入力文字列を、文字列型変数 line に読み込ませる ことが出来ます。後は, try and catch の try { ... } を使って、
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); try { System.out.println("Frau ?"); String line = reader.readLine(); System.out.println(line); } catch (IOException e) { System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } }というようになります。import java.io.*; も忘れずに。さて、このプログラムの 実行結果は例えば,
banana:~/java% java Drill Frau ? 安藤です。 安藤です。
みたいになりますね。
さて、プログラムは相変わらず単純なものですが、 単純だと思ってなめていると後で痛い目に会うかもしれません。こんな単純な プログラムにおいても字下げをちゃんと行ないましょう。
字下げはプログラムの構造を見やすくするためです。つまり、ある { (中括弧開く) がどの } (中括弧閉じる) に対応しているのかを見易くするためです。そのためには、 字下げだけでは不十分かも知れません。コメント文 を使いましょう。コメント文については教科書の 76 ページを見て下さい。 コメント文を使って書き直すと上のプログラムは、
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); try { System.out.println("Frau ?"); // 問題を出す。 String line = reader.readLine(); // 答を読み込む。 System.out.println(line); } catch (IOException e) { // try の終り System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } // main メソッドの終り } // Drill クラスの終り
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); try { System.out.println("Frau ?"); // 問題を出す。 String line = reader.readLine(); // 答を読み込む。 if (line.equals("女")) { // もし答が "女" ならば System.out.println("正解です!"); // 正解. } else { // そうでないならば、 System.out.println("はずれ. 正解は「女」です."); // 不正解. } // if の終り } catch (IOException e) {// try の終り。 System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } // main メソッドの終り } // Drill クラスの終り
問題が 1 問だけしかないのはつまんないですね。別の単語を入れてみましょう。 とりあえず問題は 3 問: Frau (女), Baum (木), Stein (石) にします。
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); try { System.out.println("Frau ?"); // 問題を出す。 String line = reader.readLine(); // 答を読み込む。 if (line.equals("女")) { // もし答が "女" ならば System.out.println("正解です!"); // 正解. } else { // そうでないならば、 System.out.println("はずれ. 正解は「女」です."); // 不正解. } // if の終り System.out.println("Baum ?"); // 問題を出す。 line = reader.readLine(); // 答を読み込む。 if (line.equals("木")) { // もし答が "木" ならば System.out.println("正解です!"); // 正解. } else { // そうでないならば、 System.out.println("はずれ. 正解は「木」です."); // 不正解. } // if の終り System.out.println("Stein ?"); // 問題を出す。 line = reader.readLine(); // 答を読み込む。 if (line.equals("石")) { // もし答が "石" ならば System.out.println("正解です!"); // 正解. } else { // そうでないならば、 System.out.println("はずれ. 正解は「石」です."); // 不正解. } // if の終り } catch (IOException e) {// try の終り。 System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } // main メソッドの終り } // Drill クラスの終り
実行結果は
banana:~/java% java Drill Frau ? 女 正解です! Baum ? 男 はずれ. 正解は「木」です. Stein ? 石 正解です!のようになります。
今度は、正解の数を数えましょう。count という int型の 変数を用意して、正解が出るたびに、1 づつ足していきます。そして、 最後に正解数を出力させましょう。プログラムは、
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); int count = 0; // 正解数を数える変数 (0 に初期化) try { System.out.println("Frau ?"); // 問題を出す。 String line = reader.readLine(); // 答を読み込む。 if (line.equals("女")) { // もし答が "女" ならば System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は「女」です."); // 不正解. } // if の終り System.out.println("Baum ?"); // 問題を出す。 line = reader.readLine(); // 答を読み込む。 if (line.equals("木")) { // もし答が "木" ならば System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は「木」です."); // 不正解. } // if の終り System.out.println("Stein ?"); // 問題を出す。 line = reader.readLine(); // 答を読み込む。 if (line.equals("石")) { // もし答が "石" ならば System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は「石」です."); // 不正解. } // if の終り System.out.println("正解数は 3 問中 " + count + " 問です."); // 正解数の出力 } catch (IOException e) {// try の終り。 System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } // main メソッドの終り } // Drill クラスの終り
のようになります。実行結果は、以下のようになるでしょう。
banana:~/java% java Drill Frau ? 男 はずれ. 正解は「女」です. Baum ? 木 正解です! Stein ? 岩 はずれ. 正解は「石」です. 正解数は 3 問中 1 問です.
もうほとんど目的は達成されているんだけれども、最後に正解数に応じて 「よくできました (^_^) 」、「まあまあかな (-_-)」、 「もっとがんばりましょう (>_<)」と出力してみましょう。
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); int count = 0; // 正解数を数える変数 (0 に初期化) try { System.out.println("Frau ?"); // 問題を出す。 String line = reader.readLine(); // 答を読み込む。 if (line.equals("女")) { // もし答が "女" ならば System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は「女」です."); // 不正解. } // if の終り System.out.println("Baum ?"); // 問題を出す。 line = reader.readLine(); // 答を読み込む。 if (line.equals("木")) { // もし答が "木" ならば System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は「木」です."); // 不正解. } // if の終り System.out.println("Stein ?"); // 問題を出す。 line = reader.readLine(); // 答を読み込む。 if (line.equals("石")) { // もし答が "石" ならば System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は「石」です."); // 不正解. } // if の終り System.out.println("正解数は 3 問中 " + count + " 問です."); // 正解数の出力 if (count == 3) { // 正解数 3 なら System.out.println("よくできました (^_^)"); } else if (count == 2) { // 正解数 2 なら System.out.println("まあまあかな (-_-)"); } else { // 正解数 <= 1 なら System.out.println("もっとがんばりましょう (>_<)"); } } catch (IOException e) {// try の終り。 System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } // main メソッドの終り } // Drill クラスの終り
上のプログラムの実行結果は,
banana:~/java% java Drill Frau ? 男 はずれ. 正解は「女」です. Baum ? 亀 はずれ. 正解は「木」です. Stein ? 石 正解です! 正解数は 3 問中 1 問です. もっとがんばりましょう (>_<)
となります。どうですか?簡単なものから少しずつ改良を加えていけば、 複雑なプログラムもけっこう簡単に作れるものです。
先週 (6 月 6 日) には、配列とメソッドということについて学びました。 まず配列を使って、もっと実用的なドリル作りを進めましょう。
各要素がドイツ語の単語であるような配列 deutsch を用意して、
3つの単語 (Frau, Baum, Stein) をこの配列に 入れます。それは、String deutsch[] = {"Frau", "Baum", "Stein"};
とします。同様にして、japanese という文字列型配列を用意して、 deutsch 配列に入っている単語の日本語訳を、順番通りに入れます。
String japanese[] = {"女", "木", "石"};
これらの二つの配列を使うと前のプログラムは、以下のようにとても シンプルなものになります。
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); String deutsch[] = {"Frau", "Baum", "Stein"}; String japanese[] = {"女", "木", "石"}; int count = 0; // 正解数を数える変数 (0 に初期化) try { for (int i=0; i<3; i++) { // 以下を i=0,1,2 について繰り返す System.out.println(deutsch[i]); // 問題を出す。 String line = reader.readLine(); // 答を読み込む。 if (line.equals(japanese[i])) { // もし答が "女" ならば System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は" + japanese[i] + "です。"); // 不正解. } // if の終り } // for 文の終わり System.out.println("正解数は 3 問中 " + count + " 問です."); // 正解数の出力 if (count == 3) { // 正解数 3 なら System.out.println("よくできました (^_^)"); } else if (count == 2) { // 正解数 2 なら System.out.println("まあまあかな (-_-)"); } else { // 正解数 <= 1 なら System.out.println("もっとがんばりましょう (>_<)"); } } catch (IOException e) {// try の終り。 System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } // main メソッドの終り } // Drill クラスの終り
ここで、配列の添字を for文を使って動かしていることに 注意して下さい。
次にやることはそんなに難しいことではありません。ただ、単語の 数をもっと増すだけです。とりあえず、
String deutsch[] = {"Frau", "Baum", "Stein", "Strasse", "Bein", "Wasser", "gut"}; String japanese[] = {"女", "木", "石", "通り", "足", "水", "良い"};
とします。後はプログラムの中にあるの問題数を表す 3 を 6 に変えれば良いので すが、今後もっと単語数を増すかも知れませんね。ですから、3 とか 6 とかの 「定数」では、後で変更を加えたときに修正が面倒です。ですから、問題数を表す 数は変数とした方が便利です。その変数を N としましょう。
int N = 6;
と最初の方に書いておいて、その行より下で問題数を参照する部分は N で置きかえます。
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); String deutsch[] = {"Frau", "Baum", "Stein", "Strasse", "Bein", "Wasser", "gut"}; String japanese[] = {"女", "木", "石", "通り", "足", "水", "良い"}; int N = 6; // 問題数 int count = 0; // 正解数を数える変数 (0 に初期化) try { for (int i=0; i<N; i++) { // 以下を i=0,1,2,..., N について繰り返す System.out.println(deutsch[i]); // 問題を出す。 String line = reader.readLine(); // 答を読み込む。 if (line.equals(japanese[i])) { // System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は" + japanese[i] + "です。"); // 不正解. } // if の終り } // for 文の終わり System.out.println("正解数は" +N + "問中 " + count + " 問です."); // 正解数の出力 if (count/(1.0*N) >= 0.8) { // 正解数 8 割以上なら System.out.println("よくできました (^_^)"); } else if (count/(1.0*N) >= 0.6) { // 正解数 6〜8割なら System.out.println("まあまあかな (-_-)"); } else { // 正解数 6 割未満なら System.out.println("もっとがんばりましょう (>_<)"); } } catch (IOException e) {// try の終り。 System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } // main メソッドの終り } // Drill クラスの終り
もうこれ以上改善の余地がないほど良くできたプログラム (自画自賛) なので すが、先週の実習では, 「メソッド」について学びました。今度はメソッドを 使ってもっとエレガントなプログラムに仕上げることにしましょう。
上のプログラムをながめてみると、
for (i=0; i<N; i++) { 出題と解答; } 採点;
というような構造になっているのが分かりますね。この「出題と解答」というのと、 「採点」をそれぞれ一つのメソッドにしてしまいたいですね。「出題と解答」 の方をメソッドにするのは少し難しいので「採点」の方をメソッドにしましょう。
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); String deutsch[] = {"Frau", "Baum", "Stein", "Strasse", "Bein", "Wasser", "gut"}; String japanese[] = {"女", "木", "石", "通り", "足", "水", "良い"}; int N = 6; // 問題数 int count = 0; // 正解数を数える変数 (0 に初期化) for (int i=0; i<N; i++) { // 以下を i=0,1,2,..., N について繰り返す try { System.out.println(deutsch[i]); // 問題を出す。 String line = reader.readLine(); // 答を読み込む。 if (line.equals(japanese[i])) { // System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は" + japanese[i] + "です。"); // 不正解. } // if の終り } catch (IOException e) {// try の終り。 System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } // for 文の終わり saiten(count, N); } // main メソッドの終り public static void saiten (int seikai, int N) { // saiten メソッド System.out.println("正解数は" +N + "問中 " + seikai + " 問です."); // 正解数の出力 if (seikai/(1.0*N) >= 0.8) { // 正解数 8 割以上なら System.out.println("よくできました (^_^)"); } else if (seikai/(1.0*N) >= 0.6) { // 正解数 6〜8割なら System.out.println("まあまあかな (-_-)"); } else { // 正解数 6 割未満なら System.out.println("もっとがんばりましょう (>_<)"); } } } // Drill クラスの終り
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); String words[][] = { {"Frau","女"}, {"Baum", "木"}, {"Stein", "石"}, {"Strasse", "通り"}, {"Bein", "足"}, {"Wasser", "水"}, {"gut", "良い"} }; int count = 0; // 正解数を数える変数 (0 に初期化) for (int i=0; i<words.length; i++) { /* 以下を i=0,1,2,..., words.lengthについて繰り返す */ try { System.out.println(words[i][0]); // 問題を出す。 String line = reader.readLine(); // 答を読み込む。 if (line.equals(words[i][1])) { // System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は" + words[i][1] + "です。"); // 不正解. } // if の終り } catch (IOException e) {// try の終り。 System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } // for 文の終わり saiten(count, words.length); } // main メソッドの終り public static void saiten (int seikai, int N) { // saiten メソッド System.out.println("正解数は" +N + "問中 " + seikai + " 問です."); // 正解数の出力 if (seikai/(1.0*N) >= 0.8) { // 正解数 8 割以上なら System.out.println("よくできました (^_^)"); } else if (seikai/(1.0*N) >= 0.6) { // 正解数 6〜8割なら System.out.println("まあまあかな (-_-)"); } else { // 正解数 6 割未満なら System.out.println("もっとがんばりましょう (>_<)"); } } } // Drill クラスの終り
import java.io.*; public class Drill { public static void main(String[] args) { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in),1); if (args.length != 1) { System.out.println("使用法: java Drill d または、"); System.out.println(" java Drill j"); System.exit(0); } int parity =0; if (args[0].equals("d")) { parity = 0; } else if (args[0].equals("j")) { parity = 1; } else { System.out.println("無効なパラメータです。"); System.exit(0); } String words[][] = { {"Frau","女"}, {"Baum", "木"}, {"Stein", "石"}, {"Strasse", "通り"}, {"Bein", "足"}, {"Wasser", "水"}, {"gut", "良い"} }; int count = 0; // 正解数を数える変数 (0 に初期化) for (int i=0; i<words.length; i++) { /* 以下を i=0,1,2,..., words.lengthについて繰り返す */ try { System.out.println(words[i][parity]); // 問題を出す。 String line = reader.readLine(); // 答を読み込む。 if (line.equals(words[i][(parity+1)%2])) { // System.out.println("正解です!"); // 正解. count = count +1; // count に 1 を足す。 } else { // そうでないならば、 System.out.println("はずれ. 正解は" + words[i][1] + "です。"); // 不正解. } // if の終り } catch (IOException e) {// try の終り。 System.out.println(e); } catch (NumberFormatException e) { System.out.println("入力が正しくありません。"); } } // for 文の終わり saiten(count, words.length); } // main メソッドの終り public static void saiten (int seikai, int N) { // saiten メソッド System.out.println("正解数は" +N + "問中 " + seikai + " 問です."); // 正解数の出力 if (seikai/(1.0*N) >= 0.8) { // 正解数 8 割以上なら System.out.println("よくできました (^_^)"); } else if (seikai/(1.0*N) >= 0.6) { // 正解数 6〜8割なら System.out.println("まあまあかな (-_-)"); } else { // 正解数 6 割未満なら System.out.println("もっとがんばりましょう (>_<)"); } } } // Drill クラスの終り