コンピュータプログラミングにおいて、switch文は式の値(つまり変数の評価)に基づいて実行制御を変更する選択制御フロー機構です。switch文はif文に似ていますが、真偽値のみで分岐するのではなく、任意の数の値で分岐します。構文はプログラミング言語によって異なりますが、ほとんどの命令型言語では、ここで説明する意味を持つ文がswitch文として提供されています。多くの場合、キーワードで表されますが、言語によっては、、、などのバリエーションを使用するものもあります。switchcaseselectinspect
場合によっては、switch文の使用は、同等のif-then-else文の連続よりも優れていると考えられています。その理由は次のとおりです。
通常、switch文には以下の要素が含まれます。
selectに多くの場合変数名である式が続きます。制御式または制御変数。case)と値(または複数の値)と、その値に対して実行するコードで始まります。PL /IやRexxなどの一部の言語では、制御式が省略されている場合、各選択肢はwhenブール式を含む句で始まり、その式が真と評価される最初のケースで一致が発生します。これはif-then-else構造に似ています。breakようにするには、各セクションの最後にキーワード(など)を付けます。default通常、オプションのデフォルトケースが許可されており、多くの場合、`default` 、 `default`、` otherwisedefault`などのキーワードによって指定されますelse。他のどのケースも制御式に一致しない場合、制御はこのセクションに分岐します。C言語などの一部の言語では、どのケースも一致せず、デフォルトセクションが省略されている場合、ステートメントは何も実行しませんが、PL/Iなどの他の言語ではエラーが発生します。switch文には主に2つの種類があり、フォールスルーをサポートする非構造型と、フォールスルーをサポートしない構造型があります。
Pascalのような言語における構造化されたswitch文では、switch文の開始位置から選択されたcaseに制御が移り、caseの終了位置でswitch文の終了位置に制御が移ります。これはif-then-else条件文のように動作しますが、真偽値だけでなく、より多くの値に基づいて分岐できます。複数の値に対して同じコードを実行できるようにするため(コードの重複を避けるため)、構文ではcaseごとに複数の値を指定できます。
C言語(およびより一般的にはFortranの計算型gotoに影響を受けた言語)における非構造化switch文は、 goto文のように動作します。制御はswitch文の開始位置からcaseセクションに分岐し、その後、ブロック終了文またはswitch文の終了まで継続します。制御が1つのcaseに分岐した後、次の分岐にも継続する場合、その制御フローはフォールスルーと呼ばれ、複数の値に対して同じコードに分岐することが可能になります。
ケースをキーワード (つまりbreak) で終了することでフォールスルーは防止されますが、よくある間違いは、誤ってキーワードを省略してしまい、意図しないフォールスルーが発生し、バグにつながることです。そのため、多くの人がこの言語機能を危険だと考えており、[ 3 ]フォールスルーコードは、 lintなどのコード品質ツールから警告を受けることがよくあります。
JavaScriptなどの一部の言語はフォールスルーのセマンティクスを保持していますが、他の言語ではフォールスルーを排除または制限しています。特にC#では、ブロックが空でない限り、すべてのブロックはbreakまたはで終了する必要がreturnあり、これによりフォールスルーは複数の値からの分岐に限定されます。
言語によっては、オプションでフォールスルーが提供される場合があります。たとえば、Perl はデフォルトではフォールスルーしませんが、continueキーワードを使用して明示的にフォールスルーすることで、意図しないフォールスルーを防ぐことができます。同様に、Bash はで終了するとデフォルトではフォールスルーしませんが、または を使用すると;;フォールスルーを許可します[ 4 ]。;&;;&
フォールスルーに依存する switch 文の例として、Duff のデバイスがあります。
一部の言語では、複雑なcase式(静的な値だけでなく)を許容することで、より動的な分岐動作を実現できます。ただし、これは特定のコンパイラ最適化を妨げるため、パフォーマンスよりも柔軟性が優先される動的言語でより一般的です。
例えば、PHPやRubyでは、定数を制御式として使用でき、その定数に一致すると評価される最初のcase文が実行されます。以下のPHPコードでは、switch式は単純にtrueなので、最初にtrueとなるcase式が選択されます。
switch ( true ) { case ( $x == 'hello' ) : foo (); break ; case ( $z == 'howdy' ) : break ; }この機能は、1つの変数を複数の値と比較するのではなく、複数の変数を1つの値と比較する場合にも役立ちます。
switch ( 5 ) { case $x : break ; case $y : break ; }COBOLもこの形式をEVALUATEステートメントでサポートしています。PL/Iも同様の動作をサポートしており、制御式を省略することで、最初にWHEN真と評価された式が実行されます。
Rubyでは、等価性の扱い方のおかげで===、case式を使って変数のクラスをテストできます。例えば:
case input when Array then puts 'input is an Array!' when Hash then puts 'input is a Hash!' end一部のプログラミング言語は、switch文を値に評価する機能をサポートしています。
ケース式は、少なくともALGOL-Wまで遡る言語でサポートされています。[ 5 ] ALGOL-Wでは、整数式が評価され、次に式のリストから目的の式が評価されました。
J := case I of ( 3.14 , 2.78 , 44 8.9 ) ; A := case DECODE ( C ) - 128 of ( " A " , " B " , " C " , " D " , " E " , " F " ) ;ケース式をサポートする他の言語には、SQL、Standard ML、Haskell、Common LISP、およびOxygeneなどがあります。
switch式( Java SE 12で導入) は値に評価されます。また、case L->右辺が単一の式である新しい形式の case ラベルもあります。これにより、フォールスルーが防止され、ケースが網羅的であることが要求されます。Java SE 13 ではステートメントyieldが導入され、Java SE 14 では switch 式が標準言語機能になります。[ 6 ] [ 7 ] [ 8 ]例:
int dayCount = switch ( month ) { case JAN , MAR , MAY , JUL , AUG , OCT , DEC -> 31 ; case APR , JUN , SEP , NOV -> 30 ; case FEB -> { if ( year % 400 == 0 ) { yield 29 ; } else if ( year % 100 == 0 ) { yield 28 ; } else if ( year % 4 == 0 ) { yield 29 ; } else { yield 28 ; } } };Rubyもこれらのセマンティクスをサポートしています。例えば:
catfood = case when cat.age < = 1 junior when cat.age > 10 senior else normal end多くのプログラミング言語では、例外処理においてswitch文のような仕組みが実装されています。これは、ブロック内で例外が発生した場合、例外の種類に応じて別の分岐が選択されるというものです。例外が発生しない場合のデフォルト分岐が用意されている場合もあります。初期の例としてはModula-3があり、これはTRY...EXCEPT構文を使用し、各EXCEPTケースを定義します。これはDelphi、Scala、Visual Basic .NETにも見られます。
以下のコードはC言語のswitch文です。 がage1の場合は「You're one." と出力します。 が3の場合は「You're three. You're three or four." と出力します。 C++ageのswitch文はC言語と同じように動作します。
#include <stdio.h>void printAge ( unsigned int age ) { switch ( age ) { case 0 : printf ( "あなたは新生児です!" ); break ; case 1 : printf ( "あなたは1歳です。" ); break ; case 2 : printf ( "あなたは2歳です。" ); break ; case 3 : printf ( "あなたは3歳です。" ); case 4 : printf ( "あなたは3歳か4歳です。" ); break ; default : printf ( "あなたは4歳以上です!" ); } }C2Y以降、switch文では捕捉するケースの範囲を指定できるようになりました。この機能は、C言語のGCC拡張機能として初めて登場しました。
#include <stddef.h> #include <stdio.h> #include <uchar.h>void writeUnicode ( char32_t c ) { switch ( c ) { // [0, 0x7F] の範囲の任意の値に一致します( 0 ... 0x7Fを含む) : putchar ( c ); break ; // [0x80, 0x7FF] の範囲の任意の値に一致します (0x80 ... 0x7FF を含む) : putchar ( 0xC0 + c >> 6 ) ; putchar ( 0x80 + c & 0x3f ) ; break ; // [ 0x800, 0xFFFF] の範囲の任意の値に一致します( 0x800 ... 0xFFFF を含む) : putchar ( 0xE0 + c >> 12 ) ; putchar ( 0x80 + ( c >> 6 ) & 0x3f ); putchar ( 0x80 + ( c >> 12 )); break ; default : unreachable (); } }Python (3.10.6 以降) は、matchおよびcaseキーワードをサポートしています。[ 9 ] [ 10 ] [ 11 ] [ 12 ]フォールスルーは許可されていません。if 文の条件とは異なり、orキーワードを使用してケースを区別することはできません。はC のcase _と同等です。default
letter : str = input ( "文字を入力してください: " ) . strip ()[ 0 ] . casefold () match letter : case "a" | "e" | "i" | "o" | "u" : print ( f "文字 ' { letter } ' は母音です!) case "y" : print ( f "文字 ' { letter } ' は母音である可能性があります。) case _ : print ( f "文字 ' { letter } ' は母音ではありません!)以下はPascalの例です。
case someChar of 'a' : actionOnA ; 'x' : actionOnX ; 'y' , 'z' : actionOnYandZ ; else actionOnNoMatch ; end ;Oxygene Pascal方言では、switch文を式として使用できます。
var i : Integer := case someChar of 'a' : 10 ; 'x' : 20 ; 'y' : 30 ; else - 1 ; end ;以下はシェルスクリプトの例です。
case $someChar in a ) actionOnA ; ;; x ) actionOnX ; ;; [ yz ]) actionOnYandZ ; ;; * ) actionOnNoMatch ;; esacアセンブリ言語におけるswitch文:
switch: cmp ah , 00h je a cmp ah , 01h je b jmp swtend ; ケースが一致しないか、ここに「デフォルト」コードがありますa: push ah mov al , 'a' mov ah , 0Eh mov bh , 00h int 10h pop ah jmp swtend ; 「break」と同等b: push ah mov al , 'b' mov ah , 0Eh mov bh , 00h int 10h pop ah jmp swtend ; 「break」と同等... swtend:switch文を使用する代わりに、以下のような方法があります。
スティーブン・クリーネは、 1952年の著書『メタ数学入門』の中で、ケース関数(if-then-else関数はその最も単純な形式)が原始的な再帰関数であることを正式に証明し、その中で「ケースによる定義」という概念を次のように定義している。
#F. 関数φは次のように定義される
- φ(x 1 , ... , x n ) =
- φ 1 (x 1 , ... , x n ) の場合、Q 1 (x 1 , ... , x n ) 、
- . . . . . . . . . . . .
- φ m (x 1 , ... , x n ) は Q m (x 1 , ... , x n ) の場合です。
- φ m+1 (x 1 , ... , x n ) それ以外の場合、
ここで、Q 1、... 、Q mは相互に排他的な述語である(または φ(x 1、... 、x n ) は適用される最初の節によって与えられた値を持つ)は、φ 1、...、φ m+1、Q 1、...、Q m+1に関して原始再帰的である。
—スティーブン・クリーネ、[ 14 ]
クリーネは、ブール型の再帰関数「sign-of」sg( )と「not sign of」~sg( )を用いてこの証明を与えている(クリーネ 1952:222-223)。前者は入力が正の場合は1を返し、入力が負の場合は-1を返す。
Boolos-Burgess-Jeffreyは、「事例による定義」は相互に排他的かつ網羅的でなければならないという追加的な指摘をしている。彼らもまた、この関数の原始的な再帰性の証明を提供している(Boolos-Burgess-Jeffrey 2002:74-75)。
if-then-else はマッカーシー形式の基礎であり、その使用は原始的な再帰とミュー演算子の両方を置き換える。
初期のFortranコンパイラは、多分岐のための計算型goto文をサポートしていました。初期のALGOLコンパイラは、「指定式」のリストを含むSWITCHデータ型をサポートしていました。goto文はswitch変数を参照し、インデックスを指定することで目的の分岐先へ分岐することができました。しかし、経験を積むにつれて、単一の入口と出口を持つ、より形式的な多分岐構造が必要であることが認識されるようになりました。BCPL、ALGOL-W、ALGOL-68などの言語は、この構造の形式を導入し、それは現代の言語にも受け継がれています。