私は独学のプログラマーです。私は約1.5年前にプログラミングを始めました。今、私は学校でプログラミングクラスを始めました。プログラミングクラスは1/2年間ありましたが、今はさらに1/2になります。
クラスでは、C++でプログラミングすることを学習しています(これは、始める前に、私がすでにかなりよく使用することを知っていた言語です)。
このクラスでは何の問題もありませんでしたが、明確な解決策を見つけることができなかった問題が繰り返し発生しています。
問題は次のようになります(疑似コード):
_ do something
if something failed:
handle the error
try something (line 1) again
else:
we are done!
_
このコードはユーザーに数値の入力を求め、入力が有効になるまで入力を続けます。 cin.fail()
を使用して、入力が無効かどうかを確認します。 cin.fail()
がtrue
の場合、cin.clear()
およびcin.ignore()
を呼び出して、ストリームから引き続き入力を取得できるようにする必要があります。
このコードは
EOF
をチェックしないことを知っています。私たちが書いたプログラムは、そうすることを期待されていません。
これが私が学校での課題の1つでコードを書いた方法です。
_for (;;) {
cout << ": ";
cin >> input;
if (cin.fail()) {
cin.clear();
cin.ignore(512, '\n');
continue;
}
break;
}
_
私の先生は、私はbreak
とcontinue
をこのように使うべきではないと言っていました。彼は、代わりに通常のwhile
または_do ... while
_ループを使用するように提案しました。
break
とcontinue
を使用することが、この種のループを表す最も簡単な方法であるように私には思えます。私は実際にそれについてかなり長い間考えましたが、より明確な解決策を実際には思い付きませんでした。
彼は私に次のようなことをして欲しかったと思います:
_do {
cout << ": ";
cin >> input;
bool fail = cin.fail();
if (fail) {
cin.clear();
cin.ignore(512, '\n');
}
} while (fail);
_
私にとって、このバージョンは追跡するためのfail
と呼ばれる変数もあり、入力エラーのチェックが一度だけではなくtwiceで行われるため、かなり複雑に思えます。
また、次のようなコードを記述できることもわかりました(短絡評価を悪用しています)。
_do {
cout << ": ";
cin >> input;
if (fail) {
cin.clear();
cin.ignore(512, '\n');
}
} while (cin.fail() && (cin.clear(), cin.ignore(512, '\n', true);
_
このバージョンは他のバージョンとまったく同じように機能します。 break
またはcontinue
を使用せず、cin.fail()
テストは1回だけ実行されます。しかし、このように「短絡評価ルール」を乱用するのは私には適切ではないようです。私の先生もそれを望んでいないと思います。
この問題は、cin.fail()
チェックだけに当てはまるわけではありません。私はbreak
とcontinue
を他の多くの場合に使用しましたが、条件が満たされるまでコードセットを繰り返す必要があり、条件が満たされない場合も何かを実行する必要があります(呼び出しなど) cin.clear()
のcin.ignore(...)
およびcin.fail()
の例)。
私はコース全体でbreak
とcontinue
を使い続けてきましたが、私の教師はこれについて不平を言うのをやめました。
これについてのあなたの意見は何ですか?
私の先生は正しいと思いますか?
この種の問題を表すより良い方法を知っていますか?
Ifステートメントは少し異なるので、入力が成功したときに取得します。
for (;;) {
cout << ": ";
if (cin >> input)
break;
cin.clear();
cin.ignore(512, '\n');
}
それも短いです。
先生が好むかもしれない短い方法を示唆しています:
cout << ": ";
while (!(cin >> input)) {
cin.clear();
cin.ignore(512, '\n');
cout << ": ";
}
あなたが努力しなければならないのは、 生のループを回避する です。
複雑なロジックをヘルパー関数に移動すると、突然物事がより明確になります。
bool getValidUserInput(string & input)
{
cout << ": ";
cin >> input;
if (cin.fail()) {
cin.clear();
cin.ignore(512, '\n');
return false;
}
return true;
}
int main() {
string input;
while (!getValidUserInput(input)) {
// We wait for a valid user input...
}
}
for(;;)
が悪いのはそれほど多くありません。次のようなパターンほど明確ではありません。
while (cin.fail()) {
...
}
またはSjoerdが言ったように:
while (!(cin >> input)) {
...
}
このようなものの主な対象者は、あなたが仲間のプログラマーであると考えてみましょう。そのようにして最後に休憩をとった理由を覚えていない、または継続して休憩を飛び越えた自分の将来のバージョンを含みます。あなたの例からのこのパターン:
for (;;) {
...
if (blah) {
continue;
}
break;
}
...他のパターンと比較してシミュレーションするには、数秒の追加の思考が必要です。これは難しい規則ではありませんが、continueを使用してbreakステートメントをジャンプするのは面倒で、役に立たないので賢く、ループの最後にbreakステートメントを配置することは、たとえそれが機能したとしても、異常です。通常、break
とcontinue
の両方を使用して時期尚早ループまたはループの反復を回避します。そのため、最後にそれらのいずれかを見ると、少し奇妙に感じられます。そうではありません。
それ以外に、良いもの!
学校のロジックインストラクターはいつも、ループに入る入り口と出口は1つだけあるべきだと言って、それを脳に打ち込みました。そうでない場合は、スパゲッティコードの取得を開始し、デバッグ中にコードがどこに行くのかを知りません。
現実には、コードの読みやすさが非常に重要であるため、他の誰かがコードの問題を修正またはデバッグする必要がある場合でも、簡単に実行できるようになっています。
また、この外観を何百万回も実行しているためパフォーマンスの問題である場合は、forループの方が高速かもしれません。テストはその質問に答えます。
C++は知りませんが、コード例の最初の2つは完全に理解しました。 continueがforループの終わりに行き、それを再びループすると仮定しています。 whileの例は完全に理解しましたが、for(;;)の例よりも理解しやすかったです。 3つ目は、それを理解するためにいくつかの思考作業を行わなければならないでしょう。
読みやすかった(c ++がわからない)私と、ロジックインストラクターがwhileループ1を使用すると言っていたことを説明します。