switch文またはlong if…elseチェーンを使用する必要がありますか?


36

多くの場合、switchステートメントについて聞いたとき、長いif ... elseチェーンを置き換える方法として先送りされています。しかし、switchステートメントを使用する場合、if ... else以外の場合に書くよりも多くのコードを書いているようです。また、すべての呼び出しのすべての変数を同じスコープに保持するなど、他の問題もあります

ここに私が通常書くフローを表すいくつかのコードがあります(diamのおかげで

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

if (which == 0) {
    comment = "You look so much better than usual.";
} else if (which == 1) {
    comment = "Your work is up to its usual standards.";
} else if (which == 2) {
    comment = "You're quite competent for so little experience.";
} else {
    comment = "Oops -- something is wrong with this code.";
}

それから彼らは私にこれをこれと取り替えてほしい:

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

switch (which) {
    case 0:  
             comment = "You look so much better than usual.";
    break;
    case 1:  
             comment = "Your work is up to its usual standards.";
    break;
    case 2:  
             comment = "You're quite competent for so little experience.";
    break;
    default: 
             comment = "Oops -- something is wrong with this code.";
}

はるかに厄介な構文でより多くのコードのようです。しかし、switchステートメントを使用することには本当に利点がありますか?


あー 確かにそれはかさばりますが、Cファミリでのみです。caseステートメントの構文は非常にugいためです。
メイソンウィーラー

5
この種のことは、stackoverflowでよく議論されています。- stackoverflow.com / questions / 449273

1
可能な限り両方を避ける必要があります。ルックアップの対象が関数またはクラスであっても、データ構造を作成してルックアップを行う方がはるかに優れています。
ケビンクライン

切り替えは、少なくとも.netでは高速です。Javaについては知りません。
ケナード14年

ケースとメソッドのディクショナリと「if」ディクショナリにリファクタリングできる
パベルヤーマロビッチ

回答:


57

この特定の状況では、両方のように私には思えるifとはcase貧弱な選択肢です。私は単純な配列を使用します:

String comments[] = {
    "You look so much better than usual.",
    "Your work is up to its usual standards.",
    "You're quite competent for so little experience."
};

String comment = comments[(int)(Math.random() * 3)];

副次的な注意事項として、通常、をハードコーディングするのではなく、配列のサイズに基づいて乗数を計算する必要があり3ます。

ケース/スイッチ使用するタイミングについては、ifステートメントのカスケードとの違い(または少なくとも1つの大きな違い)はswitch、値の数と密度に基づいて半自動で最適化できるのに対して、ifステートメントのカスケードはコンパイラーから離れることです選択肢はほとんどありませんが、記述したとおりにコードを生成し、一致する値が見つかるまで次々に値をテストします。実際のケースが3つしかない場合、それはほとんど問題になりませんが、十分な数がある場合、重要になる可能性があります。


その例は、BTWの例にすぎません。コンパイラがそのように最適化できることを知りませんでした
TheLQ

6
@JBRWilkinson。この場合、範囲外の値はコンパイラーのバグを介してのみ可能です。これは多くの時間を費やすことを嫌います(結果をテストするためのコードのバグは、生成コードの場合とほぼ同じです)。範囲外の値が実際の懸念事項である状況(たとえば、インデックスが他のコードから受信されていた場合)では、最初に境界をチェックし、チェック後にのみインデックスとして使用します。
ジェリーコフィン

4
この答えは例について非常に具体的であると思いますが、質問はそれよりも一般的
でした...- Khelben

3
@Khelben:あなたは答え全体を読むことを気にしなかったように思えます。最後の段落では、より広範な問題に対処します。ただし、問題があります。ステートメントまたはステートメントのカスケードのいずれかが適切であると考える状況はほとんどありません。ほとんどの場合、それらはある種のマップ/配列タイプの(中程度の)代替品であり、後者の1つを直接使用する方が良いでしょう。caseif
ジェリーCo

1
@Titou:コンパイラーが完全に頭脳死んでいない限り、配列はコンパイル時に一度構築され、その後は静的構造を使用するだけです。たとえば、CまたはC ++でこれを実行してstatic constいる場合、常に存在することを保証するためにその配列を作成する必要があります(ただし、質問には言語が指定されていないため、答えにも1つも仮定しないようにしました)。
ジェリーコフィン

23

if...else if...チェーンの問題は、私がそれを読みに行くときif、プログラムが何をしているかを理解するために、すべての単一の条件を調べなければならないことです。たとえば、次のようなものがあります。

if (a == 1) {
    // stuff
} else if (a == 2) {
    // stuff
} else if (a == 3) {
    // stuff
} else if (b == 1) {
    // stuff
} else if (b == 2) {
    // stuff
}

(明らかに、このような少数のステートメントについては、それほど悪くはありません)

すべてのステートメントを読むことなく、条件変数を途中で変更したことを知る方法がありません。ただし、switch1つの条件変数のみに制限されるため、何が起きているか一目でわかります。

ただし、1日の終わりには、どちらも、switchまたはのチェーンを好まないでしょうif...else if。多くの場合、より良い解決策は、元の質問や多態性(言語がサポートしている場合)のような場合の何らかの種類のジャンプテーブルまたは辞書です。もちろん、常に可能とは限りませんがswitch、最初のステップとして回避するソリューションを探します...


4
ポリモーフィズムには、コードが断片化されるという短所があり、単一の場所にある場合と比べて理解が難しくなります。したがって、それに変更する前に多少somewhatすることがあります。

ジャンプテーブル/辞書がスイッチより優れているのはなぜですか?
Titou

14
switch (which) {
  case 0: comment = "String 1"; break;
  case 1: comment = "String 2"; break;
  case 2: comment = "String 3"; break;
  default: comment = "Oops"; break;
}

このタイプのスイッチケースを記述する上記の方法は、かなり一般的です。かさばる場合にスイッチケースを感じた理由は、あなたの体が1行しかないためであり、スイッチケースではbreakステートメントも必要でした。そのため、スイッチケースの本体サイズはif elseの2倍でした。より実質的なコードでは、breakステートメントは本文にあまり追加しません。単一行の本文の場合、caseステートメントと同じ行にコードを記述するのが一般的です。

他の人がすでに述べたように、スイッチのケースは意図をより明確にします。単一の変数/式の値に基づいて決定をしたいのです。私のコメントは純粋に読みやすさの観点からであり、パフォーマンスベースではありません。


1
メソッドにスイッチを配置し、各ケースreturnに適切な文字列を指定すると、breakステートメントを削除できます。
ロバートハーヴェイ14年

ソリューションも最速です。
Titou

8

この場合、switchステートメントはコードの意図により明確に一致します。単一の値に基づいて実行するアクションを選択します。

一方、ifステートメントは読みにくいです-何が起こっているのかを確認するために、それらすべてを確認する必要があります。私にとっては、精神的に解析する必要が少ないため、コードが少なくなります(文字数がわずかに多い場合でも)。


8

私は、この特定の場合には文字列の配列の方が優れているが、一般的にはelseifのチェーンよりもswitch / caseステートメントを使用する方がよいというジェリーに同意します。読みやすく、時にはコンパイラーがそのように最適化するより良い仕事をすることができますが、別の利点もあります:それはデバッグするのがずっと簡単です。

そのスイッチを押すと、一度に複数のifステートメントを1つずつ慎重にステップオーバーするのではなく、1回ステップするだけで正しいブランチに到達します。やり直す。


3

このような場合は切り替えが好きです。コードのポイントにはるかにマッチし、入力値ごとに異なるステートメントを実行します。if..else同じ効果を達成するために、より「トリック」のような役割を果たします。

switch 文もきれいです。それらすべてにタイプミスを隠すのは簡単です ==

また、Cの大きなブロックの場合、切り替えは高速です。

else..if範囲(1から100の間、これを行う、100から200の間)のようなものがある場合、またはCで文字列のような要素(他の言語で可能)を切り替えようとする場合により適切です。どちらも同じです。

Cでプログラミングするときは、多くのスイッチを使用する傾向があります。


2

効率的で簡潔なものを選び、自分がやったことだけでなく、その理由を文書化します。

コードは再訪できますが、必ずしも元の作者によってではありません。

存在しないコードを前向きに考えているため、ある実装を別の実装よりも慎重に選択する場合があります。


2

私は一般的にどちらのアプローチも好きではありません。長いスイッチまたはifステートメントは、オブジェクト指向の抽象化にリファクタリングされるように頼みます(ただし、あなたの例では、長いではなく短いと分類します)。

私は個人的にこの種のコードを別のヘルパーメソッドにラップします。

private string GetInsult()
{
    int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

    switch (which) {
        case 0: return "You look so much better than usual.";
        case 1: return "Your work is up to its usual standards.";
        case 2: return "You're quite competent for so little experience.";
        default: return "Oops -- something is wrong with this code.";
    }
}

public void Foo()
{
    string comment = GetInsult();
    Print(comment);
}

スイッチを別のメソッドに配置すると、returnステートメントをswitchステートメント内に直接配置でき(少なくともc#で)、breakステートメントの必要性がなくなり、コードが読みやすくなります。

そして、これはif / else if / else ifアプローチよりもはるかに優れています。


3
私は物事を解決するために「itいように見えるので別の方法でそれを置く」方法を個人的に嫌います。メソッドリストが乱雑になり、見栄えが悪くなります。A)コードがどこかに複製されているか、B)他の場所で役立つ可能性がある場合にのみこれを行います
-TheLQ

1
どのメソッドリスト?そして、なぜあなたのメソッドリストはあなたのコードが混乱しているよりも混乱しているのですか?「すべてを一度に見ることができるように、すべてを1つの方法で保持する」という時代
サラ

@TheLQ私は一般的にあなたに同意しますが、この場合、「コメント=」は確かにピートの提案によって考慮されます。
Titou

0

Pythonには、if / elif / elseが便利なため、switchステートメントはありません。

a = 5

if a==1:
    print "do this"
elif a == 2:
    print "do that"
elif a == 3:
    print "do the other"
elif 3 < a < 9:
    print "do more"
elif 9 <= a < 15:
    print "do nothing"
else:
    print "say sorry"

シンプルでしょ?


Elifいくつかの文字が欠けているif文です。間違いなくもっとif switchステートメントよりもステートメントのです。Pythonにスイッチがないという事実は、(私のように)それらを嫌っている人に、彼らが単独ではないと思い込ませます。
ダンローゼンスターク

Pythonのフォーマットはstackoverflowで機能しますが、programmers.stackexchange.comでは機能しません:(
クリストファー・マーハン

metaそれが既知のトピックでない限り、あなたは彼らに警告するべきです。私に証人を与えてくれてありがとう。
ダンローゼンスターク

屋、彼らはそれが時に起こっているを発見meta.programmers.stackexchange.com/questions/308/...
クリストファーマハン

1
@Yar、ウィキペディアの管理者の日々を思い出させます...ああ、ジョイ (私はまだ完全にトピック外ですか?)
クリストファー・マーハン

0

C / C#スタイルをswitch特に迷惑にしているものの1つは、case値がリテラルであるという主張です。VB / VB.NETの優れた点の1つは、select/case各ケースを任意のブール式にすることができることです。 それは便利です。相互に排他的な一連のブール式が役立つ場合が多い限り、一連のif / else ifsはより柔軟であり、入力と読み取りがより効率的であることは言うまでもありません。

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