プログラミング指南

  1. はじめに

    結構長いプログラムをエラーなしで書くというのは大変な作業です。 ここでは、全然 (ほとんど) エラーが出ないようなプログラムを書く 技法を学びましょう。


  2. プログラムの仕様を決める

    プログラミングの例題として、「ドイツ語ドリル」を作ります。「ドリル」って いうのは僕が小学生の頃、宿題でやらされた問題集のようなもので、 「算数ドリル」とか「漢字ドリル」というようなものがありました。(今大学生の 君たちが小学生の頃こういうものをやったかどうかは知りません。)

    それでこれから作るプログラムはドイツ語の単語の学習のための「ドリル」 みたいなものです。具体的には、

    "画面にドイツ語の単語を表示して、ユーザにその日本語の意味を入力して もらう。もし正解ならば、「正解です。」と表示して、誤りならば、 正解を表示する。これを、数個の単語について繰り返す。"

    というようなものです。
  3. 少しずつ書く

    仕様通りのものをいきなり全部書くのは大変です。 仕様通りでなくても良いので、とりあえず動くものを書きましょう。 例えば、

        public class Drill {
            public static void main(String[] args) {
                 System.out.println("Frau ?");
            }
        }
        

    のようなものです。(ちなみに Frau というのは、ドイツ語で「女性」とか 「女」という意味です。) これをコンパイルして実行すると、

        banana:~/java% java Drill
        Frau ?
        

    となるはずです。


  4. 入力できるようにする。
    今度は上のプログラムを改良して、入力が出来るようにしましょう。 入力は,
    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 ?
    安藤です。
    安藤です。
    

    みたいになりますね。


  5. 字下げとコメント文

    さて、プログラムは相変わらず単純なものですが、 単純だと思ってなめていると後で痛い目に会うかもしれません。こんな単純な プログラムにおいても字下げをちゃんと行ないましょう。

    字下げはプログラムの構造を見やすくするためです。つまり、ある { (中括弧開く) がどの } (中括弧閉じる) に対応しているのかを見易くするためです。そのためには、 字下げだけでは不十分かも知れません。コメント文 を使いましょう。コメント文については教科書の 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 クラスの終り
    

  6. もうちょっと改良
    こんどは、ユーザによって入力された答が正しいかどうかを判定するように プログラムを改良しましょう。文字列の比較には, line 変数にある文字列と "女" という文字列を比較して等しければ "正解です!" と出力して、 そうでなければ, "はずれ. 正解は 「女」 です." と出力させるようにします。 line 変数に入っている文字列と別の文字列との比較は line.equals("別の文字列") という関数 (メソッド) を使います。この関数は, line に入っている文字列と "別の文字列" が等しいときに true を返し, そうでないとき false を返します。

    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 クラスの終り
    

  7. もっと改良

    問題が 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 ?
         石  
         正解です!
    
    のようになります。
  8. 正解数を数えるように改良

    今度は、正解の数を数えましょう。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 問です.
    

  9. 磨きをかける

    もうほとんど目的は達成されているんだけれども、最後に正解数に応じて 「よくできました (^_^) 」、「まあまあかな (-_-)」、 「もっとがんばりましょう (>_<)」と出力してみましょう。

    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 問です.
    もっとがんばりましょう (>_<)
    

    となります。どうですか?簡単なものから少しずつ改良を加えていけば、 複雑なプログラムもけっこう簡単に作れるものです。


  10. 配列を使おう!

    先週 (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文を使って動かしていることに 注意して下さい。

  11. もっと単語を!

    次にやることはそんなに難しいことではありません。ただ、単語の 数をもっと増すだけです。とりあえず、

     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 クラスの終り
    
  12. メソッドを使おう!

    もうこれ以上改善の余地がないほど良くできたプログラム (自画自賛) なので すが、先週の実習では, 「メソッド」について学びました。今度はメソッドを 使ってもっとエレガントなプログラムに仕上げることにしましょう。

    上のプログラムをながめてみると、

     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 クラスの終り
    
    
  13. 6月13日には2次元の配列というのを学びました。ドイツ語の単語とその日本語訳の ペアで二次元配列にしてみましょう。それと、配列名.length で配列の長さを 表すのでしたね。それも使ってみましょう。
    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 クラスの終り
    
    
  14. そういえば、「コマンドライン引数」というのも学びました。これを使って、 今度は、
    日本語の単語が与えられて、ドイツ語の訳を答える問題
    ドイツ語の単語が与えられて、日本語の訳を答える問題
    を切り換えましょう。コマンドライン引数で、d が与えられたら 「日本語->ドイツ語」, j が与えられたら 「ドイツ語->日本語」と いう問題を出題します。
    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 クラスの終り
    
    
    
  15. まだ、問題の出し方に愛想が無いかもしれませんが、このへんで終わりにし ましょう。では、Enjoy programming! Viel Spass noch !

安藤 和敏
Last modified: Fri May 19 23:45:04 JST 2000