数学クイズプログラム - 「Javaの芸術と科学」のプログラミング演習第5章第6章

私は次のプログラムを書きました:数学クイズ。これは、書籍「Art and Science of Java」のプログラミング演習第5章第6章です。

プログラムは機能しているようです。しかし、私が改善することができるあらゆる分野(あらゆる悪い習慣など)に関するあらゆる提案に感謝します。

下記にリストされている基準を満たす2つの数字を無作為に選ぶより効率的でエレガントな方法があるかどうかにも興味があります。

プログラムはこれらの要件を満たす必要があります。

•名前付き定数としてコード化された一連の5つの質問 - 算術問題 - を尋ねるべきです。

•各質問は、2つの数字だけを含む1つの加法または減法の問題で構成されるべきです。

•問題 - 加算または減算 - は、質問ごとにランダムに選択する必要があります。

•答えを含めて、関係する数字のどれも0未満にならないようにする 20以上 - これらの制約の範囲内で、プログラムは ランダムに番号を付けます。

•プログラムは生徒に各質問に3回答える機会を与えます。生徒が正しい答えを出した場合、プログラムはその事実を適切な祝福の方法で示すべきです。

•生徒が3回試行しても答えが得られない場合、プログラムは答えを出して別の問題に進みます。

/*
 * File: MathQuiz.java
 * ---------------------
 */

import acm.program.*;
import acm.util.*;

public class MathQuiz extends ConsoleProgram {

    private static final int NUMBER_OF_QUESTIONS = 5;

    public void run() {

        println("Welcome to MathQuiz");

// Starts a function which will ask questions 

        for (int i=0; i < NUMBER_OF_QUESTIONS; i++) {

// Draws a random number between 0 and 20 

            int x = getRandomX();

// Draws a random sign - plus or minus with a probability of 50 per cent            

            String sign = getPlusOrMinus(); 

// Initializes a second variable which will be used for second random number

            int y = 0;

// Initializes a variable where the good answer to the arithmetic problem will be kept      

            int goodanswer = 0;

// Draws a second random number between 0 and 20-x (method getRandomYPlus) if the drawn sign is + or between 0 and x (method getRandomYMinus) if the drawn sign is -
// I have created two methods because that numbers have to be such so that the answer will not be greater than 20 or less than zero 

            if (sign == "+") {

                y = getRandomYPlus(x);

                goodanswer = x + y;

            } else {

                y = getRandomYMinus(x);

                goodanswer = x-y;

            }

// Poses an arithmetic problem with the two random numbers and a random sign

            int answer = readInt("What is " + x + sign + y + "? ");

// Initializes a variable which will count the number of answers

            int count = 0;

// Students gets three chances to give a correct answer 

            while (answer != goodanswer && count !=3) {

                answer = readInt("That`s incorrect - try a different answer: ");

                count ++;
            }

            if (answer == goodanswer) {

                println("You got it!");

// After three wrong answers program gives a correct answer and moves to another problem                

            } else {

                println ("No, the answer is " + goodanswer + ".");


            }
        }

    }

// Method which draws a random number between 0 and 20 

    private int getRandomX() {

        int x = 0;

        x = rgen.nextInt(0,20);

        return x;
    }


// Method which draws a random sign - plus or minus - with a probability of 50 per cent

    private String getPlusOrMinus() {

        String sign = null;

        sign = rgen.nextBoolean() ? "+" : "-";

        return sign;
    }

// Draws a second random number between 0 and 20-x (method getRandomYPlus) if the drawn sign is + or between 0 and x (method getRandomYMinus) if the drawn sign is -
// I have created two methods because that numbers have to be such so that the answer will not be greater than 20 or less than zero 


    private int getRandomYPlus(int x) {

        int y = 0;

        y = rgen.nextInt(0,20-x); 

        return y;
    }

    private int getRandomYMinus(int x) {

        int y = 0;

        y = rgen.nextInt(0,x);

        return y;
    }

// Creates an instance variable for random number generator 

    private RandomGenerator rgen = new RandomGenerator();

}
3
nl ru de

4 答え

最大の問題は、最大数のサイズ(20)を複数の場所にハードコーディングしたことです。ラウンド数と同じように、これは一定であるべきです。


ワイルドカードを使用してインポートを無効にする必要があります。常に使用したい特定のクラスだけをインポートしてください。そうでなければ、2つのパッケージが同じ名前のクラスを宣言したときに名前の衝突を考えるのに苦労するでしょう。


RandomGeneratorの初期化をクラスの先頭に移動して、人々が変数 rgen が何であるかを推測する必要がないようにします。また、 finalstatic にすることもできます。これは変更されず、(おそらく)クラスのすべてのインスタンスに同じインスタンスを使用できるからです。


使用しない値に変数を初期化しないでください。

private int getRandomX() {

    int x;

    x = rgen.nextInt(0,20);

    return x;
}

または

private int getRandomX() {

    int x = rgen.nextInt(0,20);

    return x;
}

またはin a function even:

private int getRandomX() {
    return rgen.nextInt(0,20);
}

そうすれば、人々はゼロが何を意味するのかを理解する必要はありません。実際の初期化を忘れているとコードがコンパイルされないため、デバッグにも役立ちます。

Fまたはexample, this code won't compile, because goodanswer isn't initialized in the else block.

        int y;
        int goodanswer;

        if (sign == "+") {

            y = getRandomYPlus(x);

            goodanswer = x + y;

        } else {

            y = getRandomYMinus(x);

        }

ただし、 int goodanswer = 0; のままにしていると、コードはコンパイルされて実行されますが、正しく機能しないため、バグを見つけるまでに時間がかかります。

1
追加された

もういい答えだ。 1点追加します。

sign == "+"

Is a (potiential) bug. See String equals in Java

より良いのは booleanisPlus )にサインを保存することでしょう、あるいはもっと良いのは明示的な enum を使うことです。

例えば:

enum Sign
{
  PLUS, MINUS
}

それからあなたはあなたのサインである明示的なタイプを持っています、そしてあなたは使うことができます

if (sign == Sign.PLUS)
1
追加された
  • Way too much whitespace. You don't need a blank line between each line of code.

  • Way too many comments. Comments should explain why decisions were made, not what the code is doing. The code already says what it does. If it's too complex to read easily, you need to fix the code, not add a comment.

  • Don't ever compare strings with ==. Always use equals().

  • Instance variable declarations belong at the top of the class, not the bottom or interspersed in your methods.

  • Use whitespace consistently. For instance, !=3 should be != 3. int i=0 should be int i = 0.

  • this.rgen.nextInt(min, max) can be left inline. You don't need to declare three separate wrapper methods for it.

  • Use final aggressively to reduce the cognitive load of readers.

  • Multi-word variable names should use camelCase, not lowercase. (goodAnswer, not goodanswer).

  • Although y and goodAnswer both rely on the sign, I would argue that they're separate computations. For clarity, they might be better served in separate methods.

  • 20 is a magic number, and should be a constant.

  • count should be more descriptive - maybe attempts?

  • It's convention to compare < instead of !=, just in case something assigns count to be bigger than 3. If it did, your loop would go on forever.

  • Bug - you're giving four attempts, and the requirements say you should be giving three. You should start count at 1, since you ask the first time right before you assign count.

上記のアイデアを含めるためにコードをリファクタリングしようと努力した場合、次のようになります。

public final class MathQuiz extends ConsoleProgram {

    private static final int NUMBER_OF_QUESTIONS = 5;
    private static final int BIGGEST_POSSIBLE_NUMBER = 20;

    private final RandomGenerator rgen = new RandomGenerator();


    public void run() {
        this.println("Welcome to MathQuiz");

        for (int i = 0; i < NUMBER_OF_QUESTIONS; i++) {
            final int x = this.rgen.nextInt(0, BIGGEST_POSSIBLE_NUMBER);
            final String sign = this.rgen.nextBoolean() ? "+" : "-";
            final int y = this.computeY(x, sign);
            final int goodAnswer = this.computeAnswer(x, sign, y);

            int answer = this.readInt("What is " + x + sign + y + "? ");
            int attempts = 1;
            while ((answer != goodAnswer) && (attempts < 3)) {
                answer = this.readInt("That`s incorrect - try a different answer: ");
                attempts++;
            }

            if (answer == goodAnswer) {
                this.println("You got it!");
            } else {
                this.println ("No, the answer is " + goodAnswer + ".");
            }
        }
    }

    /**
     * @return a value {@code y} such that {@code y} and the correct answer to the mathematical
     * operation are both less than {@link BIGGEST_POSSIBLE_NUMBER}.
     */
    private int computeY(final int x, final String sign) {
        return sign.equals("+") ? this.rgen.nextInt(0, BIGGEST_POSSIBLE_NUMBER - x) : this.rgen.nextInt(0, x);
    }

    private int computeAnswer(final int x, final String sign, final int y) {
        return sign.equals("+") ? x + y : x - y;
    }

}
1
追加された
IMO、 goodAnswercorrectAnswer に変更する必要があります。
追加された 著者 StarDotStar,

私はあなたの提案を徹底的に分析しました、そしてそれらの大部分の理由を見ます。しかし、私はいくつかのケースで追加の説明のために素晴らしいでしょう:

  1. Use final aggressively to reduce the cognitive load of readers.

    Do you mean by that that I should use "final" agresively because if a reader sees "final" he does not have to worry that the variable will change? In other words: one thing less to keep track of in a program?

    And that, I presume, makes such a program easier to follow?

  2. You have added a lot of "this" to the program. What is the reason for that?

  3. sign == "+" Is a (potiential) bug. Better would be to store the sign in a boolean (isPlus), or better, use an explicit enum.

    Can`t I just change "==" to "equals"?

  4. The refactored version of my code has got fewer methods that mine. For instance you have not created a method for int x but you have created one for int y.

教授によるプログラミング方法論に関するオンライン講義を見た後。 Mehren Sahami私は方法が多いほど良いという印象を受けました。メソッドを作成することで、読者に不要な複雑さを隠します。

int xのメソッドを作成しないことにしたのはなぜですか?

メソッドを作成するかどうかを決定するときに、どの基準を使用しますか。

1
追加された