Switch statement
This article needs additional citations for verification. (April 2013) |
In computer programming, a switch statement is a selection control flow mechanism that changes execution control based on the value of an expression (i.e. evaluation of a variable). A switch statement is similar to an if statement but instead of branching only on true or false, it branches on any number of values. Although the syntax varies by programming language, most imperative languages provide a statement with the semantics described here as the switch statement. Often denoted with the keyword switch
, some languages use variations such as case
, select
, or inspect
.
Value
[edit]Sometimes, use of a switch statement is considered superior to an equivalent series of if-then-else statements because it is:
- Easier to understand
- And consequently easier to maintain: at least partially since it's fixed depth.
- Easier to debug
- For example, setting breakpoints on code vs. a call table, if the debugger has no conditional breakpoint capability.
- Easier to verify
- that all values are handled since a compiler can warn if a value is not handled.
- Can execute faster
- An optimized implementation may execute much faster because it is often implemented as a branch table.[1] When implemented as such, a switch statement embodies a perfect hash.
- An optimizing compiler such as GCC or Clang may compile a switch statement into either a branch table or a binary search.[2] A branch table allows the program to determine which branch to execute with a single calculation instead of comparing values in a sequence. A binary search takes only a logarithmic number of comparisons, measured in the number of cases in the switch statement. Generally, the only way to determine whether the code was optimized in this way, is by analyzing the compiler output such as assembly or machine code.
- Less complex
- In terms of a control-flow graph, a switch statement consists of two nodes (entrance and exit), plus one edge between them for each option. By contrast, a sequence of if-then-else statements has an additional node for every case other than the first and last, together with a corresponding edge. The resulting control-flow graph for the sequences of "if"s thus has many more nodes and almost twice as many edges, without adding useful information.
Elements
[edit]Typically, a switch statement involves:
- Verb
- Starts with a control verb such as
select
which is followed by an expression which is often a variable name; the control expression or control variable.
- Cases
- Subsequent branch alternative sections each start with a keyword (i.e.
case
) plus a value (or multiple values) along with code to execute for the value(s). In some languages, i.e. PL/I and Rexx, if the control expression is omitted then each alternative begins with awhen
clause containing a Boolean expression and a match occurs for the first case for which that expression evaluates true; similar to an if-then-else structure.
- In a language with fall through behavior, such as C, each section ends with a keyword (such as
break
) if that section should not fall through.
- Default
- An optional default case is typically allowed; often via a keyword such as
default
,otherwise
, orelse
. Control branches to this section when none of the other cases match the control expression. In some languages, such as C, if no case matches and the default section is omitted, the statement does nothing, but in others, like PL/I, an error occurs.
Fall through
[edit]Two main variations of the switch statement include unstructured which supports fall through and structured which does not.
For a structured switch, as in Pascal-like languages, control jumps from the start of the switch statement to the selected case and at the end of the case, control jumps to the end of the switch statement. This behaves like an if–then–else conditional but supports branching on more than just true and false values. To allow multiple values to execute the same code (avoiding duplicate code), the syntax permits multiple values per case.
An unstructured switch, as in C (and more generally languages influenced by Fortran's computed goto), acts like goto. Control branches from the start of the switch to a case section and then control continues until either a block exit statement or the end of the switch statement. When control branches to one case, but continues into the subsequent branch, the control flow is called fall through, and allows branching to the same code for multiple values.
Fall through is prevented by ending a case with a keyword (i.e. break
), but a common mistake is to accidentally omit the keyword; causing unintentional fall through and often a bug. Therefore, many consider this language feature to be dangerous,[3] and often fall through code results in a warning from a code quality tool such as lint.
Some languages, such as JavaScript, retain fall through semantics, while others exclude or restrict it. Notably, in C# all blocks must be terminated with break
or return
unless the block is empty which limits fall through only for branching from multiple values.
In some cases, languages provide optional fall through. For example, Perl does not fall through by default, but a case may explicitly do so using a continue
keyword; preventing unintentional fall through. Similarly, Bash defaults to not falling through when terminated with ;;
, but allows fall through[4] with ;&
or ;;&
instead.
An example of a switch statement that relies on fall through is Duff's device.
Case expression evaluation
[edit]Some languages allow for a complex case expression (not just a static value); allowing for more dynamic branching behavior. This prohibits certain compiler optimizations, so is more common in dynamic languages where flexibility is prioritized over performance.
For example, in PHP and Ruby, a constant can be used as the control expression, and the first case statement that evaluates to match that constant is executed. In the following PHP code, the switch expression is simply the true value, so the first case expression that is true is the one selected.
switch (true) {
case ($x == 'hello'):
foo();
break;
case ($z == 'howdy'): break;
}
This feature is also useful for checking multiple variables against one value rather than one variable against many values.
switch (5) {
case $x: break;
case $y: break;
}
COBOL also supports this form via its EVALUATE
statement. PL/I supports similar behavior by omitting the control expression, and the first WHEN
expression that evaluates as true is executed.
In Ruby, due to its handling of ===
equality, the case expression can be used to test a variable’s class. For example:
case input
when Array then puts 'input is an Array!'
when Hash then puts 'input is a Hash!'
end
Result value
[edit]Some languages support evaluating a switch statement to a value.
Case expression
[edit]The case expression is supported by languages dating at least as far back as ALGOL-W.[5] In ALGOL-W, an integer expression was evaluated, which then evaluated the desired expression from a list of expressions:
J := case I of (3.14, 2.78, 448.9);
A := case DECODE(C)-128 of ("A", "B", "C", "D", "E", "F");
Other languages supporting the case expression include SQL, Standard ML, Haskell, Common LISP, and Oxygene.
Switch expression
[edit]The switch expression (introduced in Java SE 12) evaluates to a value. There is also a new form of case label, case L->
where the right-hand-side is a single expression. This also prevents fall through and requires that cases are exhaustive. In Java SE 13 the yield
statement is introduced, and in Java SE 14 switch expressions become a standard language feature.[6][7][8] For example:
int ndays = 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 also supports these semantics. For example:
catfood =
case
when cat.age <= 1
junior
when cat.age > 10
senior
else
normal
end
Exception handling
[edit]A number of languages implement a form of switch statement in exception handling, where if an exception is raised in a block, a separate branch is chosen, depending on the exception. In some cases a default branch, if no exception is raised, is also present. An early example is Modula-3, which use the TRY
...EXCEPT
syntax, where each EXCEPT
defines a case. This is also found in Delphi, Scala, and Visual Basic .NET.
Examples
[edit]C
[edit]The following code is a switch statement in C. If age
is 1, it outputs "You're one.". If age
is 3, it outputs "You're three. You're three or four.".
switch (age) {
case 1: printf("You're one."); break;
case 2: printf("You're two."); break;
case 3: printf("You're three.");
case 4: printf("You're three or four."); break;
default: printf("You're not 1, 2, 3 or 4!");
}
Python
[edit]Python (starting with 3.10.6) supports the match
and case
keywords.[9][10][11][12] It doesn't allow fall through. Unlike if statement conditions, the or
keyword cannot be used to differentiate between cases. case _
is equivalent to default
in C.
letter = input("Enter a letter: ").strip()[0].casefold()
match letter:
case "a" | "e" | "i" | "o" | "u":
print(f"Letter {letter} is a vowel!")
case "y":
print(f"Letter {letter} may be a vowel.")
case _:
print(f"Letter {letter} is not a vowel!")
Pascal
[edit]The following is an example in Pascal:
case someChar of
'a': actionOnA;
'x': actionOnX;
'y','z':actionOnYandZ;
else actionOnNoMatch;
end;
In the Oxygene dialect of Pascal, a switch statement can be used as an expression:
var i : Integer := case someChar of
'a': 10;
'x': 20;
'y': 30;
else -1;
end;
Shell script
[edit]The following is an example in Shell script:
case $someChar in
a) actionOnA; ;;
x) actionOnX; ;;
[yz]) actionOnYandZ; ;;
*) actionOnNoMatch ;;
esac
Assembler
[edit]A switch statement in assembly language:
switch:
cmp ah, 00h
je a
cmp ah, 01h
je b
jmp swtend ; No cases match or "default" code here
a:
push ah
mov al, 'a'
mov ah, 0Eh
mov bh, 00h
int 10h
pop ah
jmp swtend ; Equivalent to "break"
b:
push ah
mov al, 'b'
mov ah, 0Eh
mov bh, 00h
int 10h
pop ah
jmp swtend ; Equivalent to "break"
...
swtend:
Alternatives
[edit]Some alternatives to using a switch statement include:
- if-then-else
- A series of if-then-else conditionals can test for each case value; one at a time. Fall through can be achieved with a sequence of if conditionals each without the else clause.
- Control table
- The logic of a switch statement can be encoded as a control table (a form of lookup table) that is keyed by the case values and each value encodes what is otherwise in the case section – as a function pointer or anonymous function or similar mechanism.
- In a language that does not provide a switch statement, such as Lua,[13] a control table provides a way to implement switch statement semantics while enabling runtime efficiency that if-then-else does not.
- Pattern matching
- Pattern matching is switch-like functionality used in many functional programming languages.
History
[edit]In his 1952 text Introduction to Metamathematics, Stephen Kleene formally proves that the case function (the if-then-else function being its simplest form) is a primitive recursive function, where he defines the notion "definition by cases" in the following manner:
"#F. The function φ defined thus
- φ(x1 , ... , xn ) =
- φ1(x1 , ... , xn ) if Q1(x1 , ... , xn ),
- . . . . . . . . . . . .
- φm(x1 , ... , xn ) if Qm(x1 , ... , xn ),
- φm+1(x1 , ... , xn ) otherwise,
where Q1 , ... , Qm are mutually exclusive predicates (or φ(x1 , ... , xn) shall have the value given by the first clause which applies) is primitive recursive in φ1, ..., φm+1, Q1, ..., Qm+1.
— Stephen Kleene, [14]
Kleene provides a proof of this in terms of the Boolean-like recursive functions "sign-of" sg( ) and "not sign of" ~sg( ) (Kleene 1952:222-223); the first returns 1 if its input is positive and −1 if its input is negative.
Boolos-Burgess-Jeffrey make the additional observation that "definition by cases" must be both mutually exclusive and collectively exhaustive. They too offer a proof of the primitive recursiveness of this function (Boolos-Burgess-Jeffrey 2002:74-75).
The if-then-else is the basis of the McCarthy formalism: its usage replaces both primitive recursion and the mu-operator.
The earliest Fortran compilers supported the computed goto statement for multi-way branching. Early ALGOL compilers supported a SWITCH data type which contains a list of "designational expressions". A goto statement could reference a switch variable and, by providing an index, branch to the desired destination. With experience it was realized that a more formal multi-way construct, with single point of entrance and exit, was needed. Languages such as BCPL, ALGOL-W, and ALGOL-68 introduced forms of this construct which have survived through modern languages.
See also
[edit]References
[edit]- ^ Guntheroth, Kurt (April 27, 2016). Optimized C++. O'Reilly Media. p. 182. ISBN 9781491922033.
- ^ Vlad Lazarenko. From Switch Statement Down to Machine Code
- ^ van der Linden, Peter (1994). Expert C Programming: Deep C Secrets, p. 38. Prentice Hall, Eaglewood Cliffs. ISBN 0131774298.
- ^ since version 4.0, released in 2009.
- ^ Wirth, Niklaus; Hoare, C. A. R. (June 1966). "A contribution to the development of ALGOL". Communications of the ACM. 9 (6): 413–432. doi:10.1145/365696.365702. S2CID 11901135. Retrieved 2020-10-07 – via Association for Computing Machinery.
- ^ "JEP 325: Switch Expressions (Preview)". openjdk.java.net. Retrieved 2021-04-28.
- ^ "JEP 354: Switch Expressions (Second Preview)". openjdk.java.net. Retrieved 2021-04-28.
- ^ "JEP 361: Switch Expressions". openjdk.java.net. Retrieved 2021-04-28.
- ^ Galindo Salgado, Pablo. "What's New In Python 3.10". Python 3.10.6 documentation. Retrieved 2022-08-19.
- ^ Bucher, Brandt; van Rossum, Guido (2020-09-12). "PEP 634 – Structural Pattern Matching: Specification". Python Enhancement Proposals. Retrieved 2022-08-19.
- ^ Kohn, Tobias; van Rossum, Guido (2020-09-12). "PEP 635 – Structural Pattern Matching: Motivation and Rationale". Python Enhancement Proposals. Retrieved 2022-08-19.
- ^ Moisset, Daniel F. "PEP 636 – Structural Pattern Matching: Tutorial". Python Enhancement Proposals. Retrieved 2022-08-19.
- ^ Switch statement in Lua
- ^ "Definition by cases", Kleene 1952:229
Further reading
[edit]- Stephen Kleene, 1952 (10th reprint 1991), Introduction to Metamathematics, North-Holland Publishing Company, Amsterdam NL, ISBN 0-7204-2103-9
- George Boolos, John Burgess, and Richard Jeffrey, 2002, Computability and Logic: Fourth Edition, Cambridge University Press, Cambridge UK, ISBN 0-521-00758-5 paperback. cf page 74-75.