ほとんどの開発者は、すべてのswitch
にbreak
sを含むcase
ステートメントを使用しています。 if-else
ステートメントの単純なセットで十分なので、switch
ステートメントの使用は不適切ですか?
switch
を使用する唯一の理由は、2つ以上のcase
sが関連しているためだと考えてもかまいません。したがって、意図的にいくつかのステートメントを「フォールスルー」させたいと思いますか?
switch
を使用しても問題ないと思う例は、データベースのスキーマを更新する場合です。したがって、バージョン1からバージョン4に更新する場合は、スキーマがバージョン2と3を最初に通過するようにする必要があります。
private void updateDatabase(int oldVersion){
switch(oldVersion){
case 1:
updateToVersion2();
case 2:
updateToVersion3();
case 3:
updateToVersion4();
}
}
switch
のNOT OKの例は、ビデオゲームでそれを使用して、押されたボタンに基づいてキャラクターを移動する場合です。 break
を押すと文字が全方向に移動しないように、case
を各LEFT
する必要があります。
private void onButtonPressed(Button button){
switch(button){
case LEFT:
moveLeft();
break;
case RIGHT:
moveRight();
break;
case UP:
moveUp();
break;
case DOWN:
moveDown();
break;
}
}
いいえ、switch
ステートメントは通常間違って使用されません。
これらは主に使用目的に使用されます。可能な入力値の小さめのセットの代替アクションを列挙します。長いif/elseチェーンよりも読みやすく、チェックされた値が適度に隣接している場合、コンパイラーは非常に効率的なコードを出力でき、代替を忘れたときに警告するエラーチェックコンパイラーを書くのが簡単です。これはすべてうまくいきます。 (継承と動的ディスパッチが望ましい場合にswitch
を使用することは、OOPをまだ十分に理解していない初心者にとってよくある間違いですが、それはより高いレベルの問題です。 )
ただし、switch
はで定義されていますほとんどの言語では不適切です。
最も一般的な使用例は、ケースごとに異なることを行うことであるため、フォールスルーではない各ケースの後にbreak
を記述するのは面倒で煩雑です。 switch
本体の後のデフォルトがbreak
であり、明示的なcase
キーワードを介してフォールスルーを注文する必要がある場合、fallthrough
ステートメントはより適切に機能します(2つのcase
sが直接隣接していない限り)。それは多くの不要な行を削除し、多くの人はbreak
を忘れて微妙なエラーを犯すことはなかったでしょう。
しかし、それについて何かをするのは今では間違いなく手遅れです。 Cはそのようにしており、その子孫のどれもそれについて何かを変更するのに十分勇敢でなかったので、当分の間、switch
はおそらくそのままです。
私はあなたの言っていることを理解していますが、これは最終的に不適切ではありません。
elses
が1つまたは2つだけの場合は、if-else
の変更で十分です。しかし、それ以上になると、switch
ステートメントはすぐに、よりクリーンで見た目が悪くなります。または...あなたが個人的に誰であるかに依存して、それはそれをさらに厄介なものに変える可能性があります。
常にすべてを打ち破る場合は厳密に表面的なものですが、特に悪いと言うことは、/*...*/
コメントと//
コメントの間で、一方が他方よりも完全に優れていると言うようなものです。長いif-else
チェーンに問題がない場合、より簡単なswitch
ステートメントの何が問題になっていますか?この質問が当てはまる場合、それは表面的なものであり、フェンスの両側にきちんと見える人がいます。これに関して大きな決まりはありません。
ただし、if-else
チェーンだけを使用するのではなく、すべてから抜け出すswitch
ステートメントに何か問題がある場合、switch
ステートメントを正当化するために何があるのですか? tすべてから抜け出す?最初の例を見てみましょう(私はBrandon
に同意しますが、シャープでした!):
private void updateDatabase(int oldVersion){
switch(oldVersion){
case 1:
updateToVersion2();
case 2:
updateToVersion3();
case 3:
updateToVersion4();
}
}
これは次のように簡単に書くことができませんでした:
private void updateDatabase(int oldVersion){
if (oldVersion > 0 && oldVersion < 4){
if (oldVersion < 3){
if (oldVersion < 2){
updateToVersion2();
}
updateToVersion3();
}
updateToVersion4();
}
}
これは化粧品的に良いですか悪いですか?それは意見の問題です。これはパフォーマンスの点で良いですか、悪いですか?技術的には、言語のコンパイル方法に応じて、導入されたint
とint
の比較が1つ追加される可能性がありますが、実際には、高水準プログラミング言語でしょうか?実際には、おそらくoldVersion
が0
より大きいことをすでに確認しているか、またはswitch
ステートメントを使用して同様のチェックを関数に追加する必要があります。
一部の人々はこれも考えています:
switch (true)
{
case variable1:
doSomething1();
break;
case variable2 && variable3:
doSomething2();
break;
case !variable4 && variabl5:
doSomething3();
break;
.
.
.
default:
doSomethingElse();
}
最終的には次のコードよりもクリーンなコードです。
if (variable1)
{
doSomething1();
}
else if (variable2 && variable3)
{
doSomething2();
}
else if (!variable4 && variable5)
{
doSomething3();
.
.
.
}
else
{
doSomethingElse();
}
(最後の2つのスニペットで中括弧が配置された場所の違いに注意してください。人々がこの方法でこれを行うと、特定の言語で進行中の議論の主題となりますが、switch
ステートメント。)
switch
ステートメントの些細なブレイクアウトブレイクアウトの使用には何の問題もありませんが、経験豊富なプログラマーは、あなたが何に反対しているかを理解できるはずです。
一部の言語( Scala JVM、Haskell、Ocamlなどにコンパイルされています)には パターンマッチング があります。これは、データ構造を分解するため、switch
より優れています。バインド変数(ローカルで一致する場合)。
たとえば、Ocamlでは、単純な [〜#〜] ast [〜#〜] データ型(asum typeまたは tagged union )は次のように表現します。
_type expr =
Num of int
| Sum of expr * expr
| Diff of expr * expr
| Prod of expr * expr
(* etc... *)
;;
_
次に、AST of 2*(3+4)
を
_ Prod (Num 2, Sum (Num 3, Num 4))
_
そしてあなたの再帰評価関数は
_let rec eval exp =
match exp with
Num x -> x
| Sum (el,er) -> (eval el) + (eval er)
| Diff (el,er) -> (eval el) - (eval er)
| Prod (el,er) -> (eval el) * (eval er)
(* etc....*)
_
一致する変数el
とer
のスコープは、_|
_で始まる上記の行のように各一致句に限定されていることに注意してください(一致する操作によってバインドされます)。
コンパイラは通常、パターンマッチングをswitch
のような構成(インデックス付きジャンプなど)とフィールド抽出に変換します。