「ブレーク」と「継続」は悪いプログラミング慣行ですか?


191

私の上司は、下手なプログラマーがループ内で使用しているbreakとさりげなく言及していcontinueます。

それらが理にかなっているので、私は常にそれらを使用します。インスピレーションをお見せしましょう:

function verify(object) {
    if (object->value < 0) return false;
    if (object->value > object->max_value) return false;
    if (object->name == "") return false;
    ...
}

ここでのポイントは、最初に関数が条件が正しいことを確認してから、実際の機能を実行することです。IMOループにも同じことが適用されます。

while (primary_condition) {
    if (loop_count > 1000) break;
    if (time_exect > 3600) break;
    if (this->data == "undefined") continue;
    if (this->skip == true) continue;
    ...
}

これにより、読みやすく、デバッグしやすくなります。しかし、私にも欠点はありません。


2
誰が何をするかを忘れることはあまりありません。

57
いいえ。どちらも後藤です。それらをいつ使用するかを知ることが重要です。ツールボックス内のツールです。明確で簡潔なコードを提供するときに使用します。
orj

67
このスタイルのコーディングに対するサポートを十分に強く表明することはできません。複数レベルのネストされた条件は、このアプローチよりもはるかに悪いです。私は通常、コーディングスタイルについては好戦的ではありませんが、これは私にとってほとんど契約違反です。
エミルH

9
明らかに、上司は(十分な)コードを書きません。彼がした場合、彼はすべてのキーワードは、(はい、でもことを知っているであろうgoto)に有用であるいくつかの例。
サヤスク

57
悪いプログラマーはブレークとコンティニューを使用しますが、良いプログラマーが使用しないという意味ではありません。悪いプログラマーはifとwhileも使用します。
ムーヴィシエル

回答:


240

ブロックの開始時に使用される場合、最初のチェックが行われたように、それらは前提条件のように機能するので、それは良いことです。

いくつかのコードを使用して、ブロックの中央で使用すると、隠されたトラップのように振る舞うので、それは悪いことです。


6
@Klaim:いくつかの出口点があるルーチンは、不十分なファクターのルーチンであると主張できます。適切にファクタリングされたルーチンは、1つのことと1つのことだけを行う必要があります。
ビットツイダー

79
@ bit-twiddler:これは非常にCっぽい考え方です。一時変数は後で変更できるため、ここから20行下の1つのタイプミスで、慎重に作成された結果が消去される可能性があります。即時のリターンは(または中断、または継続)が非常に明確である:私が読んで停止することができ、今、私はそれはおそらく変更することはできません知っているので、さらにダウン。それは私の小さな頭脳に良いです、本当にコードを簡単にうろつくことができます。
マチューM.

15
@Matthieu同意します。ブロックの目的を満たす結果を受け取ったら、ブロックを終了します。
エヴァンプライス

8
@ bit-twiddler-単一出口点の原則は、単一責任の原則とは別です。私は、チェックがWRT単独の責任を果たすことと常に分離していることに同意しません。実際、「単一の責任」は常に主観的な用語として私を襲います。たとえば、二次ソルバーでは、判別式の計算は別の責任ですか?または、二次式全体が単一の責任になりますか?私はそれがあなたがその判別式の別の用途を持っているかどうかにかかっていると主張します-そうでなければ、それを別の責任として扱うことはおそらく過剰です。
Steve314

10
その答えは経験則であり、難しい規則ではありません。それはほとんどの場合に機能しますが、あなたの文脈で理にかなっているなら、気軽に壊してください。
クライム14

87

Donald Knuthの1974年の論文Structured Programming with go to Statementsを読むgo toと、構造的に望ましいもののさまざまな使用法が説明されています。これらには、同等のステートメントbreakcontinueステートメントが含まれます(ここでの使用の多くは、go toより限定的な構造に開発されています)。あなたの上司は、クヌースを悪いプログラマーと呼ぶタイプですか?

(例が一般的。興味を私に与えられ、breakそしてcontinue一つのエントリと、コードのどの部分から1つの出口が好きな人に嫌われ、人のその種でも複数でしかめ面されているreturn文。)


8
関数とプロシージャが単一のエントリポイントと出口ポイントを持つことを好む人のほとんどは、Pascalで育ちました。パスカルは私が学んだ最初の言語ではありませんでしたが、今日までどのようにコードを構成するかに大きな影響を与えました。私のJavaコードを読むのがどれほど簡単かについて、人々はいつもコメントしています。これは、複数の出口ポイントを避け、宣言とコードを混在させることを避けるためです。メソッドの最上部で、メソッドで使用されるすべてのローカル変数を宣言するように最善を尽くします。この方法は、メソッドを簡潔にすることを強制することにより、コードの乱雑を防ぎます。
ビットツイダー

5
Pascalにはネストされた関数もありました。Just sayin '...
Shog9

6
私が覚えていることから、その昔、人々が関数で複数のreturnステートメントを好まなかった主な理由は、デバッガーがそれらを適切に処理しなかったからです。関数の最後にブレークポイントを設定するのは大変な苦労でしたが、以前のreturnステートメントのためにブレークポイントにヒットすることはありませんでした。私が最近使用しているすべてのコンパイラについて、それはもはや問題ではありません。
ダンク

28
@ bit-twiddler:それについてはよくわかりません。私は現在もPascalを使用していますが、一般的に「シングルエントリシングルエグジット」、または少なくともそのシングルエグジット部分をカーゴカルトプログラミングと考えています。私は、ツールボックス内のツールとしてBreakContinueとだけ考えてExitいます。コードを読みやすくするためにそれらを使用し、読みにくくする場合は使用しないでください。
メイソンウィーラー

2
@ bit-twiddler:それにアーメン。また、画面上に簡単に収まるブロックに到達すると、複数の出口ポイントがはるかに面倒にならないことも付け加えます。
Shog9

44

私は彼らが悪いとは思わない。それらが悪いという考えは、構造化プログラミングの時代から来ています。これは、関数には1つのエントリポイントと1つの出口ポイント、つまりreturn関数ごとに1 つだけが必要であるという概念に関連しています。

これは、関数が長く、複数のネストされたループがある場合に意味があります。ただし、関数は短くする必要があり、ループとその本体を独自の短い関数にラップする必要があります。一般に、関数に単一の出口点を強制すると、非常に複雑なロジックが発生する可能性があります。

関数が非常に短い場合、1つのループ、または最悪の場合2つのネストされたループがあり、ループ本体が非常に短い場合、a breakまたはa continueが何をするかは非常に明確です。また、複数のreturnステートメントが何をするかも明らかです。

これらの問題は、Robert C. Martinによる「Clean Code」およびMartin Fowlerによる「Refactoring」で対処されています。


12
「関数を小さくしてから、小さくしてください」-ロバートC.マーティン。これは驚くほどうまく機能することがわかりました。関数内のコードのブロックを見て、その機能を説明するコメントが必要になるたびに、説明的な名前の別の関数にそれをラップします。たとえそれがほんの数行であっても、そして一度だけ使用されたとしても。この方法により、ブレーク/継続または複数のリターンに関する問題のほとんどが解消されます。
ディマ

2
@Mikhail:サイクロマティックの複雑さは一般にSLOCと非常に強い相関関係があります。つまり、アドバイスは「長い関数を書かない」ように簡略化できます。
ジョンR.ストローム

3
単一の出口点という考え方は、広く誤解されています。昔々、関数は呼び出し元を返す必要がありませんでした。彼らは他の場所に戻ることができました。これは一般にアセンブリ言語で行われました。Fortranにはこのための特別な構造がありました。アンパサンドを前に付けたステートメント番号を渡すことができCALL P(X, Y, &10)、エラーの場合、関数は呼び出しのポイントに戻る代わりに、そのステートメントに制御を渡すことができます。
ケビンクライン

たとえば、Luaで見られる@kevincline。
Qix 14

1
@cjsimonあなたはそれを得た。関数だけが小さくあるべきではありません。クラスも小さくする必要があります。
ディマ

39

悪いプログラマーは絶対に話します(シスのように)。優れたプログラマーは、可能な限り明確なソリューションを使用します(他のすべてのものは同等です)。

breakとcontinueを頻繁に使用すると、コードを追跡しにくくなります。しかし、それらを置き換えるとコードがさらに追跡しにくくなる場合、それは悪い変更です。

あなたが与えた例は間違いなく、休憩と継続をよりエレガントなものに置き換える必要がある状況です。


はい、終了するケースの例を提供するために、条件の数を誇張しました。
ミハイル

9
提案した置換コードの例は何ですか?私はそれがガードステートメントのかなり合理的な例だと思いました。
simgineer

私には、「可能な限り最も明確な解決策」は常に...可能だと思われますか?「最も明確な」解決策がないのはなぜでしょうか。しかし、その後、私は絶対的なものではありませんので、多分あなたは正しいです。

@nocomprendeあなたが何を得ているのか分かりません。ここでの「可能」とは、最適なソリューションが存在しないことを示すものではありません。完璧で究極の明快さだけが問題ではありません。結局、主観的です。
マシュー

22

ほとんどの人は、振る舞いを簡単に予測できないため、これは悪い考えだと思います。コードを読んでいて、while(x < 1000){}x> = 1000まで実行されると仮定している場合...しかし、途中でブレークがある場合、それは当てはまらないので、あなたは本当に信頼できないループしています...

人々がGOTOを好まないのと同じ理由です。確かに、それうまく使用できますが、それはまた、コードがセクションからセクションへとランダムに跳躍する、ゴッドフルスパゲッティコードにつながる可能性があります。

自分自身で、複数の条件で壊れたループを実行するwhile(x){}場合、ブレークする必要があるときにXをfalseに切り替えます。最終的な結果は同じになり、コードを読んでいる人は誰でもXの値を切り替えたものをより詳しく見ることができます。


2
+1は非常によく言い、while(notDone){ }アプローチに対して+1(別のことができれば)を言いました。
-FrustratedWithFormsDesigner

5
ミハイル:ブレークの問題は、ループの最終条件が1か所で単純に述べられないことです。そのため、ループ後の事後条件を予測することは困難です。この些細なケース(> = 1000)では難しくありません。多くのif文と異なるレベルのネストを追加すると、ループの事後条件を判断するのが非常に難しくなります。
S.Lott

S.Lottは、頭に真っ直ぐに釘を打ちました。反復を制御する式には、反復を継続するために満たす必要があるすべての条件を含める必要があります。
ビットツイダー

6
に置き換えbreak;x=false;も、コードがより明確になるわけではありません。あなたはまだその文の本文を検索する必要があります。そして、x=false;あなたの場合、それがx=true;さらに下に当たらないことを確認する必要があります。
シェード

14
人々が「私はxを見てyを仮定しますが、zを行うとその仮定は成り立たない」と言うとき、私は「その愚かな仮定をしないでください」と思う傾向があります。多くの人々はそれを「私が見たとき、while (x < 1000)それが1000回実行されると思う」に単純化するでしょう。まあ、最初はゼロであっても、それが間違っている理由はたくさんありxます。たとえばx、ループ中に正確に1回インクリメントされ、他の方法で変更されることはないという人はいますか?あなた自身の仮定でさえ、何かが設定されx >= 1000ているからといってループが終了するわけではありません-条件がチェックされる前に範囲内に戻されるかもしれません。
Steve314

14

はい、breakステートメントなしで(またはループの途中から戻り、同じことを行う)プログラムを[再]書くことができます。ただし、追加の変数やコードの重複を導入する必要がある場合がありますが、どちらも通常はプログラムを理解しにくくします。Pascal(プログラミング言語)は、その理由から特に初心者プログラマーにとって非常に悪いものでした。上司は基本的に、Pascalの制御構造でプログラミングすることを望んでいます。もしLinus Torvaldsがあなたの靴の中にいたなら、彼はおそらくあなたの上司に中指を見せたでしょう!

コサラジュの制御構造の階層と呼ばれるコンピューターサイエンスの結果があります。これは、1973年に遡り、1974年からのクヌースの(より)有名なgotoに関する論文に記載されています。 。)1973年にS. Rao Kosarajuが証明したことは、深さnのマルチレベルブレークがあるすべてのプログラムを、追加の変数を導入せずにブレーク深度がn未満のプログラムに書き換えることができないことです。しかし、それは単なる理論上の結果だとしましょう。(追加の変数をいくつか追加するだけです!上司を喜ばせるためにそれを行うことができます...)

ソフトウェアエンジニアリングの観点からはるかに重要なのは、1995年のEric S. Robertsによる最近の論文「ループ出口と構造化プログラミング:討論の再開」http://cs.stanford.edu/people/eroberts/papers/SIGCSE- 1995 / LoopExits.pdf)。ロバーツは、彼の前に他の人によって行われたいくつかの実証研究を要約しています。たとえば、CS101タイプの学生のグループが配列内の順次検索を実装する関数のコードを書くように求められた場合、調査の著者は、break / return / gotoを使用して終了する学生について次のように述べました要素が見つかったときの順次検索ループ:

間違った解決策を生み出した[このスタイル]を使ってプログラムを試みた一人の人をまだ見つけていません。

ロバーツは次のようにも言っています。

forループからの明示的な戻り値を使用せずに問題を解決しようとした学生の方がはるかにうまくいきませんでした。その数字は、20%未満の成功率を表しています。

はい、CS101の学生よりも経験が豊富かもしれませんが、breakステートメント(または同等にループの途中からreturn / goto)を使用しなければ、最終的には、名目上はうまく構造化されていても、追加のロジックに関しては十分に毛深いコードを記述します変数とコードの複製。誰か、おそらくあなた自身が、上司のコーディングスタイルを追おうとする際に、その中に論理的なバグを入れます。

また、ここでロバーツの論文は平均的なプログラマーにとってはるかにアクセスしやすいので、Knuthの論文よりも最初に読む方が良いと言います。また、より短く、より狭いトピックをカバーしています。上司がCSタイプではなく管理職であっても、おそらくそれを上司に勧めることさえできます。


2
長年、構造化プログラミングが私のアプローチでしたが、最後の数回は、可能な限り最初に明示的な出口を完全に使用するように切り替えました。これにより、実行が高速化され、無限ループ(ハング)を引き起こす論理エラーがほぼ排除されます。
DocSalvager

9

これらの悪い習慣のいずれかを使用することは考えていませんが、同じループ内でそれらを使いすぎると、ループで使用されているロジックを再考する必要があります。控えめに使用してください。


:)はい、私が提供した例は概念的でした
ミハイル

7

あなたが与えた例は、中断も継続も必要ありません。

while (primary-condition AND
       loop-count <= 1000 AND
       time-exec <= 3600) {
   when (data != "undefined" AND
           NOT skip)
      do-something-useful;
   }

あなたの例の4行に関する私の「問題」は、それらがすべて同じレベルにあるが、異なることをするということです:いくつかの中断、いくつかは続きます...あなたは各行を読まなければなりません。

私のネストされたアプローチでは、深くなるほど、コードは「有用」になります。

ただし、ループの停止の理由(プライマリ条件以外)が見つかった場合は、ブレークまたはリターンが使用されます。トップレベルの条件でテストされる追加のフラグを使用するよりも、それを好むでしょう。ブレーク/リターンはより直接的です。さらに別の変数を設定するよりも意図を明確に示します。


+1しかし、実際にはあなたの<比較<=はOPソリューションと一致する必要があります
エルロノコ

3
ほとんどの場合、ブレーク/リターンを使用してフロー制御を管理する必要があるほど深い場合、関数/メソッドは複雑すぎます。
ビットツイダー

6

「悪さ」は、それらの使用方法に依存します。通常、ループ構造でブレークを使用するのは、アルゴリズムのリファクタリングでは保存できないサイクルを保存できる場合のみです。たとえば、特定のプロパティの値がtrueに設定されているアイテムを探すコレクションを循環します。知っておく必要があるのが、アイテムの1つでこのプロパティがtrueに設定されていることだけである場合、その結果が得られたら、ループを適切に終了するためにブレークが有効です。

ブレークを使用してもコードが特に読みやすくならない、実行する時間が短くなる、または処理のサイクルを大幅に節約できない場合は、コードを使用しないのが最善です。私は、可能な限り「最低公約数」にコーディングして、自分をフォローしている人が自分のコードを簡単に見て、何が起きているのかを確認できるようにします(私はいつもこれで成功するとは限りません)。ブレークは、奇妙な入り口/出口ポイントを導入するため、それを減らします。誤って使用すると、「goto」ステートメントを暴行するような動作をすることができます。


私は両方の点に同意します!2つ目のポイントに関しては、英語のように表示されるため、元の投稿をフォローする方が簡単だと思います。1つのifステートメントに条件の組み合わせがある場合、ifがtrueを実行するために何が起こらなければならないかを理解することはほとんど解読されます。
ミハイル

@Mikhail:あなたが提供したサンプルは、特定のリアリズムに対して少し利他的です。私が見るように、これらのサンプルは明確で簡潔で読みやすいです。ほとんどのループはそのようなものではありません。ほとんどのループには、他の何らかのロジックが実行されており、潜在的にはるかに複雑な条件があります。これらのケースでは、ブレーク/コンティニューが最適な使用法ではない場合があります。これは、読み取り時にロジックが不明瞭になるためです。
ジョエルイーサートン

4

絶対にそうではありません...はい、gotoそれはあなたのプログラムの構造を悪化させ、また制御フローを理解することも非常に難しいので、使用は悪いです。

しかし、のようなステートメントを使用するbreakcontinue、これらの日絶対に必要であり、全く悪いプログラミングの練習として考慮。

また、breakとの使用中の制御フローを理解するのは難しくありませんcontinue。以下のような構築物において声明絶対に必要です。switchbreak


2
1981年にCを初めて習得してから「continue」を使用していません。continueステートメントによってバイパスされるコードは条件付き制御ステートメントでラップできるため、これは不要な言語機能です。
ビットツイダー

12
私のコードが矢印コードにならないようにするため、これらのケースではcontinueを使用することを好みます。goto typeステートメントよりも矢印コードが嫌いです。また、「このステートメントが真の場合、このループの残りをスキップして、次の反復を続行する」と読みました。forループの開始時に非常に便利です(whileループではあまり役に立ちません)。
-jsternberg

@jsternberg勝利のために!:-)
Notinlist

3

本質的な概念は、プログラムを意味的に分析できることにあります。単一のエントリと単一の出口がある場合、可能なパスを示すために必要な計算は、分岐パスを管理する必要がある場合よりもかなり簡単です。

部分的に、この困難は、コードについて概念的に推論できることに反映されます。

率直に言って、2番目のコードは明らかではありません。何してるの?ループを「続行」しますか、それとも「次」にしますか?何も思いつきません。少なくとも最初の例は明確です。


マネージャーがそれらの使用を義務付けているプロジェクトで作業するとき、その使用を思い出すためにフローチャートを使用する必要があり、いくつかの出口パスはコードをより混乱させました
...-umlcat

あなたの「率直に」コメントは構文的です-「次」はperlのものです。通常の言語は「このループをスキップする」ことを意味するために「継続」を使用します
ミハイル

@Mik、おそらく「この反復をスキップする」の方が適切な記述でしょう。ほんとうに、IM Pedantic博士
ピート・ウィルソン

@ミハイル:もちろん。しかし、多くの言語で作業する場合、言語間で構文が一般的でない場合、エラーが発生する可能性があります。
ポールネイサン

しかし、数学はプログラミングではありません。コードは数学よりも表現力豊かです。私が取得シングルエントリー/シングル出口はフローチャートが立派に見えるが、何の費用(すなわちブレーク/リターンコードがよりよく行うことができます)で作ること?
エヴァンプライス

3

2番目のコードスニペットを次のように置き換えます

while (primary_condition && (loop_count <= 1000 && time_exect <= 3600)) {
    if (this->data != "undefined" && this->skip != true) {
        ..
    }
}

簡潔さの理由ではありません-実際にこれは読みやすく、誰かが何が起こっているのかを理解するのが簡単だと思います。一般的に、ループの条件は、身体全体に散らばっていないループ条件内に純粋に含まれている必要があります。しかし、いくつかの状況があるbreakcontinue読みやすさを助けることができるが。私が追加するbreakよりもcontinueもっと:D


「これは実際に読みやすいと思う」とは反対ですが、これは上記のコードとまったく同じ目的を達成します。私は今まで「ブレーク」を短絡演算子と考えたことはありませんでしたが、完全に理にかなっています。「一般的に、ループの条件はこれらのループ条件に純粋に含まれる必要があります」については、ループ定義に条件付きロジックを挿入する方法がないため、foreachループを処理しているときに何をしますか?
エヴァンプライス

@Evan条件はforeachループに適用されません。これは、コレクション内のすべてのアイテムを反復するだけだからです。forループは、条件付きのエンドポイントを持っていなければならないという点で類似しています。条件付きエンドポイントが必要な場合は、whileループを使用する必要があります。
エルロノコ

1
@エヴァン私はあなたのポイントを見ています-つまり、「foreachループから抜け出す必要がある場合はどうなりますか?」-まあ、breakループからの私の意見には最大1つしかありません。
エルロノコ

2

私はあなたの上司に反対します。以下のための適切な場所があるbreakcontinue、使用するには。実際、例外と例外処理が現代のプログラミング言語に導入された理由は、単にを使用してすべての問題を解決できないことですstructured techniques

サイドノートでは 、私はここで宗教的な議論を開始したくないが、あなたはこのようにも読みやすくするためにあなたのコードを再構築できます。

while (primary_condition) {
    if (loop_count > 1000) || (time_exect > 3600) {
        break;
    } else if ( ( this->data != "undefined") && ( !this->skip ) ) {
       ... // where the real work of the loop happens
    }
}

別のメモ

( flag == true )変数が既にブール値である場合、ブール値に必要な答えがあるときに発生する必要がある追加の比較を導入しているため、条件付きでの使用が個人的に嫌いです-もちろん、あなたのコンパイラがその余分な比較を最適化します。


私は何年もこの質問に悩まされてきました。あなたのやり方( 'if(flag){...}')はもっと簡潔で、多分もっと「プロフェッショナル」または「エキスパート」に見えます。しかし、そうです、それはしばしば、読者/維持者が構造の意味を思い出すために簡単に自分自身を中断しなければならないことを意味します。そして、テストの意味を確かめる。現在、私は 'if(flag == true){...}'を使用しています。というのは、それがより良いドキュメントだと思われるからです。来月?サーブを静めますか?
ピートウィルソン

2
@Pete-この用語は簡潔ではなくエレガントです。あなたが望むすべてのワッフルことができますが、あなたは、リーダ/メンテナが何A理解していないことを心配している場合booleanであるか、どのような簡潔な/エレガントな用語が意味することは多分あなたはより良いいくつかのよりスマートなメンテナ;-)雇い、その後で
ジークHansell

@Pete、私はまた、生成されたコードに関する私の声明を支持しています。式のブール値を評価する前に、フラグを定数値と比較することにより、もう1つの比較を行っています。なぜそれを必要以上に難しくするのか、フラグ変数にはすでに必要な値があります!
ジークハンセル

+1良い仕事。前の例よりもはるかにエレガントです。
エヴァンプライス

2

私はあなたの上司に同意します。サイクロマティックの複雑さが高いメソッドを生成するため、それらは悪いです。そのような方法は読みにくく、テストが困難です。幸いなことに、簡単な解決策があります。ループ本体を別のメソッドに抽出し、「継続」が「戻り」になります。「返品」の方が優れているのは、「返品」が終わったからです。現地の状態について心配する必要はありません。

「break」の場合、ループ自体を別のメソッドに抽出し、「break」を「return」に置き換えます。

抽出されたメソッドが多数の引数を必要とする場合、それはクラスを抽出するための指標です-それらをコンテキストオブジェクトに収集します。


0

複数のループ内に深くネストされている場合にのみ問題になると思います。どのループにブレークしているのかを知るのは困難です。継続を追跡することも難しいかもしれませんが、本当の痛みは休憩から来ると思います-論理を追跡するのは難しい場合があります。


2
実際、適切にインデントされているか、それらのステートメントのセマンティクスが内部化されていて、あまり眠くないかどうかを確認するのは簡単です。

@delnan-それは多くの仮定です;)
davidhaskins

2
ええ、特に最後。
マイケルK

とにかく、#1は本格的なプログラミングに必要であり、#2はプログラマと呼ばれるすべての人に期待され、#3は一般的に非常に有用です;)

一部の言語(私が知っているスクリプトのみ)がサポートされていbreak 2ます。他の人のために、一時的なブールフラグが使用されると思います
ミハイル

0

次の例のように偽装gotoとして使用されていない限り、

do
{
      if (foo)
      {
             /*** code ***/
             break;
      }

      if (bar)
      {
             /*** code ***/
             break;
      }
} while (0);

元気です。(生産コードで見られる例、meh)


このようなケースで機能を作成することをお勧めしますか?
ミハイル

はい、できなければ、単純なgotoです。DOを見ると、ジャンプをシミュレートするためのコンテキストではなく、繰り返しを考えています。
SuperBloup

ああ私は同意します、私はあなたがそれをどうするかを尋ねていました:)コンピュータ科学者はそれを繰り返し行います
ミハイル

0

これらのスタイルはどちらも好きではありません。ここに私が好むものがあります:

function verify(object)
{
    if not (object->value < 0) 
       and not(object->value > object->max_value)
       and not(object->name == "") 
       {
         do somethign important
       }
    else return false; //probably not necessary since this function doesn't even seem to be defined to return anything...?
}

私は本当にreturn機能を中止するのが好きではありません。それは悪用のように感じreturnます。

また、使用するbreakことは常に読みやすいとは限りません。

もっと良いかもしれません:

notdone := primarycondition    
while (notDone)
{
    if (loop_count > 1000) or (time_exect > 3600)
    {
       notDone := false; 
    }
    else
    { 
        skipCurrentIteration := (this->data == "undefined") or (this->skip == true) 

        if not skipCurrentIteration
        {
           do something
        } 
        ...
    }
}

ネストが少なくなり、複雑な条件が変数にリファクタリングされます(実際のプログラムでは、明らかにもっと良い名前が必要です...)

(上記のコードはすべて擬似コードです)


11
上記で入力したものよりも本当に3レベルのネストを好むでしょうか?
ミハイル

@Mikhail:はい、または条件の結果を変数に割り当てます。私はそれがはるかに簡単のロジックよりも理解することを見つけるbreakcontinue。ループを異常終了するのは奇妙に感じるだけで、私はそれが好きではありません。
FrustratedWithFormsDesigner

1
皮肉なことに、あなたは私の条件を誤解しています。continue機能をスキップして次のループに進むことを意味します。「実行を継続しない」
ミハイル

@ミハイル:ああ。あまり使わないので、読んでいるとその意味に戸惑う。私が好きではないもう一つの理由。:P更新する時間をください
...-FrustratedWithFormsDesigner

7
ネストが多すぎると読みやすさが損なわれます。私はちょうどコトだ-そして、時には、休憩を避ける/継続は、あなたのコードが何をしているかの誤解につながることができ、あなたの条件テストにあなたのロジックを反転させる必要性が、導入されました
ジークHansell

0

いいえ。問題を解決する方法であり、他の方法で解決することもできます。

現在の多くの主流言語(Java、.NET(C#+ VB)、PHP、独自の言語を書く)は、「break」と「continue」を使用してループをスキップします。どちらも「構造化されたgoto(s)」文です。

彼らがいなければ:

String myKey = "mars";

int i = 0; bool found = false;
while ((i < MyList.Count) && (not found)) {
  found = (MyList[i].key == myKey);
  i++;   
}
if (found)
  ShowMessage("Key is " + i.toString());
else
  ShowMessage("Not found.");

彼らと:

String myKey = "mars";

for (i = 0; i < MyList.Count; i++) {
  if (MyList[i].key == myKey)
    break;
}
ShowMessage("Key is " + i.toString());

「break」および「continue」コードは短く、通常「while」文は「for」または「foreach」文に変換されることに注意してください。

どちらの場合もコーディングスタイルの問題です。冗長スタイルによりコードをより細かく制御できるため、これらを使用しないことを好みます。

実際、私はこれらの文章を使用することが義務付けられているいくつかのプロジェクトで働いています。

一部の開発者は、それらは必ずしも必要ではないと思うかもしれませんが、仮想的なもので、削除する必要がある場合は、「while」と「do while」(「repeat until」、パスカル連中)も削除する必要があります;-)

結論として、たとえそれらを使用したくない場合でも、それはオプションであり、悪いプログラミング手法ではないと思います。


うるさいので申し訳ありませんが、2番目の例では、キーが見つからない場合の出力が欠落しています(もちろん、それよりずっと短く見えます)。
-FrustratedWithFormsDesigner

2
キーがリストの最後のキーである場合にのみ最初の例が機能することは言うまでもありません。
ミハイル

@FrustratedWithFormsDesigner正確に。私は、なぜその方法が望ましいのかを示すために、目的を追求しました;
umlcat

ただし、セマンティクスが異なる2つのルーチンがあります。したがって、それらは論理的に同等ではありません。
ビットツイダー

2
2番目の例には、構文と論理の2つのバグがあります。1.反復子がforループのスコープ外で宣言されていないため、コンパイルされません(したがって、文字列出力では使用できません)。2.イテレータがループ外で宣言されていても、キーがコレクション内で見つからない場合、文字列出力はリストの最後のアイテムのキーを出力します。
エヴァンプレイス

0

私は反対continueではありませんbreakが、原則的には、非常に低レベルの構成要素であり、非常に頻繁により良いものに置き換えることができると思います。

ここでは例としてC#を使用しています。コレクションを反復処理する場合を考えますが、一部の述語を満たす要素のみが必要であり、最大100回を超える反復処理は行いません。

for (var i = 0; i < collection.Count; i++)
{
    if (!Predicate(item)) continue;
    if (i >= 100) break; // at first I used a > here which is a bug. another good thing about the more declarative style!

    DoStuff(item);
}

これはかなりきれいに見えます。理解するのはそれほど難しくありません。しかし、より宣言的であることから多くを得ることができると思います。以下と比較してください:

foreach (var item in collection.Where(Predicate).Take(100))
    DoStuff(item);

たぶん、Where呼び出しとTake呼び出しはこのメソッド内にあるべきではありません。たぶん、このフィルタリングは、コレクションがこのメソッドに渡される前に行う必要があります。とにかく、低レベルのものから遠ざかり、実際のビジネスロジックに焦点を合わせると、実際に何に興味があるのか​​がより明確になります。オン。

低レベルのものはまだコードの一部に存在しますが、これを可能な限り隠したいのです。ビジネス上の問題を推論するために使用できる精神的なエネルギーが必要になるからです。


-1

Code Completeには、gotoルーチンまたはループからの複数の戻り値の使用に関する素晴らしいセクションがあります。

一般に、それは悪い習慣ではありません。breakまたはcontinue次に何が起こるかを正確に伝えます。そしてこれに同意します。

Steve McConnell(Code Completeの著者)は、さまざまなgotoステートメントを使用する利点を示すために、ほぼ同じ例を使用しています。

ただし、使いすぎbreakたりcontinue、複雑でメンテナンスできないソフトウェアにつながる可能性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.