assertおよびenforce

  1. 06:091:2を入力すると、プログラムが正常に終了することに気付くだろう。ただし、開始時刻がユーザーが入力したものと異なることに気付くかもしれない:
    09:06から1時間2分後は10:08である。

    ご覧の通り、06:09として入力された時間にもかかわらず、出力には09:06が表示されている。このエラーは、次の問題でassertを使用して検出される。

  2. 06:0915:2を入力した後、assertの失敗により、次の行に移動する:
    string timeToString(int hour, int minute) {
        assert((hour >= 0) && (hour <= 23));
        // ...
    }
    D

    このassertチェックが失敗するには、この関数が無効なhour値で呼び出されている必要がある。

    プログラム内のtimeToString()への2つの呼び出しには問題がないようだ:

    writefln("%s hours and %s minutes after %s is %s.",
             durationHour, durationMinute,
             timeToString(startHour, startMinute),
             timeToString(endHour, endMinute));
    D

    少し詳しく調査すると、バグの実際の原因が明らかになるはずだ。開始時刻を読み込む際に、時間と分の変数が入れ替わっている。

    readTime("Start time", startMinute, startHour);
    D

    このプログラミングの誤りにより、時間は09:06と解釈され、それに持続時間15:2が加算されると、無効な時間値になってしまう。

    明らかな修正は、時間と分変数を正しい順序で渡すことだ:

    readTime("Start time", startHour, startMinute);
    D

    出力:

    開始時刻? (HH:MM) 06:09
    期間? (HH:MM) 15:2
    06:09から15時間2分後は21:11である。
  3. 再び同じassertチェックだ:
    assert((hour >= 0) && (hour <= 23));
    D

    その理由は、addDuration()は23より大きい時間値を生成する可能性があるためだ。最後に余り演算を追加すると、この関数の出力の保証の1つが確保される。

    void addDuration(int startHour, int startMinute,
                     int durationHour, int durationMinute,
                     out int resultHour, out int resultMinute) {
        resultHour = startHour + durationHour;
        resultMinute = startMinute + durationMinute;
    
        if (resultMinute > 59) {
            ++resultHour;
        }
    
        resultHour %= 24;
    }
    D

    この関数には他の問題もあることに注意。例えば、resultMinuteは59より大きくなる可能性がある。次の関数は、分値を正しく計算し、関数の出力保証が確実に守られるようにしている。

    void addDuration(int startHour, int startMinute,
                     int durationHour, int durationMinute,
                     out int resultHour, out int resultMinute) {
        resultHour = startHour + durationHour;
        resultMinute = startMinute + durationMinute;
    
        resultHour += resultMinute / 60;
        resultHour %= 24;
        resultMinute %= 60;
    
        assert((resultHour >= 0) && (resultHour <= 23));
        assert((resultMinute >= 0) && (resultMinute <= 59));
    }
    D
  4. 頑張ってほしい。