Switch statement

From Wikipedia, the free encyclopedia - View original article

 
Jump to: navigation, search

Contents

In programming, a switch, case, select or inspect statement is a type of selection control mechanism that exists in most imperative programming languages such as Pascal, Ada, C/C++, C#, Java, and so on. It is also included in several other types of languages. Its purpose is to allow the value of a variable or expression to control the flow of program execution via a multiway branch (or "goto", one of several labels). The main reasons for using a switch include improving clarity, by reducing otherwise repetitive coding, and (if the heuristics permit) also offering the potential for faster execution through easier compiler optimization in many cases.

History

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. (Definition by cases)." (Kleene 1952:229)

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.

Typical syntax

In most languages, a switch statement is defined across many individual lines using one or two keywords. A typical syntax is:

Each alternative begins with the particular value, or list of values (see below), that the control variable may match and which will cause the control to go to the corresponding sequence of statements. The value (or list/range of values) is usually separated from the corresponding statement sequence by a colon or an implication arrow. In many languages, every case must also be preceded by a keyword such as case or when. An optional default case is typically also allowed, specified by a default or else keyword; this is executed when none of the other cases matches the control expression.

In languages derived from C, a break keyword is used to go to the end of the switch, thus completing execution of the switch statement. In such languages, program execution "falls through" to the statements associated with the next case in the source text when no break is present, thereby behaving like a GOTO mechanism. Notable variations on this in the C-family include C#, in which all blocks must be terminated with a break unless the block is empty (i.e. fallthrough is used as a way to specify multiple values).

The Pascal family (Object Pascal, Modula, Oberon, Ada, etc.), as well as modern BASIC dialects influenced by Pascal, do not allow fallthrough. The same goes for most functional languages and many others. Pascal-type languages instead permit any number of values per case, given as a comma-separated list, as a range, or as a combination. Perl is a language where cases do not fall through by default, but may explicitly do so using a continue keyword.

Compilation

If the range of input values is identifiably 'small' and has only a few gaps, some compilers that incorporate an optimizer may actually implement the switch statement as a branch table or an array of indexed function pointers instead of a lengthy series of conditional instructions. This allows the switch statement to determine instantly what branch to execute without having to go through a list of comparisons.

Optimized switch

To optimize a switch statement, the programmer must use a very compact range of possible values to test.[1] Sometimes it is necessary to convert the switch to a more suitable range using an inexpensive transformation. See algorithmic efficiency for an explanation of how the programmer can "assist" the compiler to make an efficient choice. See also the section 'Compiler generated branch tables' in branch table article for why optimization is not always performed as expected and how to solve this.

Checking for optimization

Normally, the only method of finding out if this optimization has occurred is by actually looking at the resultant assembly or machine code output that has been generated by the compiler (and is therefore seldom, if ever, done by HLL programmers). The first 'C' example below would be eligible for this kind of optimization if the compiler supported it (the range '0' through '9' with zero gaps without a defined case label).

Advantages

In some languages and programming environments, the use of a case or switch statement is considered superior to an equivalent series of if-else statements because it is:

Additionally, as mentioned above, an optimized implementation may:

For example, deciding program flow based on a single character's value, if correctly implemented, is vastly more efficient than the alternative, reducing instruction path lengths considerably.

When implemented as such, a switch statement essentially becomes a perfect hash

Disadvantages

When implemented with fall-through as the default path, switch/case statements are a frequent source of bugs among even experienced programmers, given that, in practice, the "break" is almost always the desired path, but not the default behavior of the switch/case construct (at least in C and Java).

Examples

The following are simple examples, written in the various languages, that use switch (or switch-like) statements to print one of several possible lines, depending on the value of an integer entered by the user.

Ada

Ada does not allow “fall through”; it uses case, when and others. Ada requires full coverage of all possible values for the type in the case statement. If a when others => case is not specified, then the code will not compile if either extra cases are specified, or missing. If at some point in the future, the definition of Digit is modified, the compiler will ensure that the programmer updates the case statement to reflect the changes to the type definition, which ensures that the program is kept up to date and helps to reduce maintenance costs. A list of values for a particular case can be combined using '|' as shown below, or a range of values may be specified using ".." to indicate the extents of the range. e.g., when 0 .. 4 => Put_Line ("Small Digits); In the example below, there is no need to check for values outside the range of 0 to 9 because the type is guaranteed to have a value within the range for the type.

    type Digit is new Integer range 0 .. 9;    n : Digit;    ...    case n is       when 0 =>          Put_Line ("You typed zero");       when 1 | 9 =>          Put_Line ("n is a perfect square");       when 2 =>          Put_Line ("n is a prime number");          Put_Line ("n is an even number");       when 3 | 5 | 7 =>          Put_Line ("n is a prime number");       when 4 =>          Put_Line ("n is a perfect square");          Put_Line ("n is an even number");       when others =>          Put_Line ("n is an even number");    end case; 

Algol 60

In Algol 60 a switch is effectively an array of labels (branch table). A switch declaration defines its values (i.e., for each index value the name of a label occurring somewhere in the program). A goto statement can specify as destination, instead of a fixed label, an "array element" of this switch, i.e., the switch identifier with, in brackets, the index.

C, C++, D, Java, PHP, ActionScript, JavaScript

In C and similarly-constructed languages, the lack of break keywords to cause fall through of program execution from one block to the next is used extensively.[citation needed] For example, if n==2, the fifth case statement will produce a match to the control variable. The next line outputs "n is an even number.". Execution then continues through the next 3 case statements and to the next line, which outputs "n is a prime number." this is a classic example of omitting the break line to allow for fall through. The break line after a case block causes the switch statement to conclude. If the user types in more than one digit, the default block is executed, producing an error message by executing the default code.

 switch (n) {   case 0:     printf("You typed zero.");     break;   case 4:     printf("n is an even number.");   case 1:   case 9:     printf("n is a perfect square.");     break;     case 2:     printf("n is an even number.");   case 3:   case 5:   case 7:     printf("n is a prime number.");     break;   case 6:   case 8:     printf("n is an even number.");     break;   default:     printf("Only single-digit numbers are allowed."); } 

Clojure

Instead of a switch/case syntax, Clojure uses a cond macro.

 (cond (= n 5) 5       (= n 3) 3        :else "n is not equal to 5 or 3") 

Common Lisp

Common Lisp has both cond, which is a generic conditional macro and case.

 (cond ((evenp x) (/ x 2))       ((oddp x) (+ (* x 3) 1))       (t "X is not an integer")) (case x       (1 2)       (2 3)       (3 4)       (otherwise 0)) 

C#

In C#, every case block that contains any statements must have a reachable end point, or triggers a compilation error. Usually, this is a break statement, but any jump statement can be used – such as return, goto or throw – or the switch can simply end with an infinite loop.[2] Case fall-through is only permitted when there are no statements between one case statement and the next. If fall-through is otherwise desired, it must be made explicit with the goto case construct. C# also allows the use of non-integer case values, such as Strings.

 switch (n) {   case 0:     Console.WriteLine("You typed zero.");     break;   case 1:   case 4:   case 9:     Console.WriteLine("n is a perfect square.");     break;   case 2:     Console.WriteLine("n is an even number.");     goto case 3;   case 3:   case 5:   case 7:     Console.WriteLine("n is a prime number.");     break;   case 6:   case 8:     Console.WriteLine("n is an even number.");     break;   default:     Console.WriteLine("Only single-digit numbers are allowed.");     break; } 

Eiffel

Eiffel's multi-branch instruction uses inspect, when, and else. It does not have the “fall through” behavior. Also, the else part is optional. However, an omitted else differs from an included, but empty else. If the else is empty and a case is processed that is not specified in one of the when parts, control passes through the else. But if the else is omitted, it is assumed that all cases should be identified in a when part. In this case an exception will occur as a result of processing a case not handled in a when part.

 inspect         n when 0 then         print ("You typed zero%N") when 1, 9 then         print ("n is a perfect square%N") when 2 then         print ("n is a prime number%N")         print ("n is an even number%N") when 3, 5, 7 then         print ("n is a prime number%N") when 4 then         print ("n is a perfect square%N")         print ("n is an even number%N") when 6, 8 then         print ("n is an even number%N") else         print ("Only single digit numbers are allowed%N") end 

Go

Like Perl, the Go programming language has an explicit fallthrough statement which can be used at the end of a case statement to indicate that control falls through next case clause in an expression "switch" statement.[3]

Haskell

Haskell's case construct, unlike C-influenced languages, has no fall-through behaviour. It is an expression which returns a value, and it can deconstruct values using pattern matching.

  case list of    (f:r) -> "Not empty, first item is " ++ show f    []    -> "List is empty!" 

OCaml, F#

OCaml and F#'s match construct is like Haskell's case above.

 (* OCaml *)  match list with    f::r -> "Not empty, first item is " ^ string_of_int f  | []   -> "List is empty!" 
 // F#  match list with  | f::r -> "Not empty, first item is " + string f  | []   -> "List is empty!" 

Pascal

Pascal does not allow “fall through”, but has ranges and comma separated literal lists.

  case age of    0,1: writeln('baby');    2,3,4: writeln('toddler');    5..12: writeln('kid');     13..19: writeln('teenager');     20..25: writeln('young');     else writeln('old');   end; 

Perl

Perl 5.10 (backported from Perl 6) has a powerful built in switch statement called given, where the cases are called when:

 use feature 'switch'; given ($foo) {     when (undef) {         say '$foo is undefined';     }     when ("foo") {         say '$foo is the string "foo"';     }     when ([1,3,5,7,9]) {         say '$foo is an odd digit';         continue; # Fall through     }     when ($_ < 100) {         say '$foo is numerically less than 100';     }     when (\&complicated_check) {         say 'a complicated check for $foo is true';     }     default {         die "I don't know what to do with $foo";     } } 

PL/I

PL/I has two types of SELECT statement and does not allow "fall through." The default action is indicated by an OTHERWISE statement; if the OTHERWISE is omitted and one of the specified cases is not chosen, the ERROR condition is raised. The end of the SELECT statement is indicated by "END".

In the first form the SELECT statement specifies an expression that is evaluated and compared to each of the expressions in each WHEN statement in turn. If a match is found the associated action is performed and the SELECT terminates. Exactly one of the alternatives or the OTHERWISE action is executed.

The second form is similar to a multiple IF-statement. No expression is specified in the SELECT statement; the arbitrary expressions in each WHEN statement are evaluated in turn, the action specified for the first true condition encountered is performed, and the SELECT terminates.

The following examples are equivalent except that the second omits the OTHERWISE.

 /* This is the first SELECT syntax */ select(a);   when(1,2) do;     /* Compound statement */     put( 'a is one or two' );     a = 99;     end;   when(3)     /* Simple statement */     put( 'a is three' );   otherwise put( 'a is not one, two, or three' );   end; /* Terminates the SELECT */ 
 /* This is the second SELECT syntax */ select;   when(a=1, a=2) do;     /* Compound statement */     put( 'a is one or two' );     a = 99;     end;   when(a=3)     /* Simple statement */     put( 'a is three' );   /* No OTHERWISE is specified in this example. */   /* The ERROR condition raised if a is not */   /* 1, 2, or 3 */   end; /* Terminates the SELECT */ 

Python

Python does not have direct language support for the switch statement; 'if'/'elif' is often used for that. However, it is possible to emulate this behaviour, through a dictionary of functions.

Here is an example that does not use a “fall through” mechanism. The default case is mimicked by using dict.get()'s fallback parameter:

 switch = {     "a": DoChoiceA,     "b": DoChoiceB,     "c": DoChoiceC,     }   switch.get(choice, DoDefaultChoice)() 

QBasic

In QBasic, the switch statement is called "Select Case", and fall-through to later blocks is not supported. The "Select Case" statement is more expressive because it allows conditionals within cases.

 SELECT CASE age         CASE IS < 12:   PRINT "Have some juice!"         CASE 13 TO 20:  PRINT "Have a soda!"         CASE IS >= 21:  PRINT "Have a beer!" END SELECT 

Ruby

Ruby doesn’t allow “fall through”; it uses case, when and else:

 case n when 0    puts 'You typed zero' when 1, 9    puts 'n is a perfect square' when 2    puts 'n is a prime number'   puts 'n is an even number' when 3, 5, 7    puts 'n is a prime number' when 4, 6, 8    puts 'n is an even number' else                 puts 'Only single-digit numbers are allowed' end 

Also can be used to assign a value, in a more compact way:

 result = case n   when 0 then 'none'   when 1..9 then 'valid'   else 'too much' end puts 'n is ' + result 

Shell script

Bash and similar shell scripting languages offer a case construct using the OR operator, |, to separate the selections, and the ) symbol to separate the list of selections from the action to be taken. Fall through is done using ;& (new since Bash 4) whereas ;; acts as a case break.

 case $n in     0)      echo 'You typed 0.';;     1|9)    echo "$n is a perfect square.";;     3|5|7)  echo "$n is a prime number.";;     4)      echo "$n is a perfect square.";&  # fall through     2|6|8)  echo "$n is an even number.";;     *)      echo 'Only single-digit numbers are allowed.';; esac 

SQL

SQL has a case/when/then/else/end expression (introduced in SQL-92). In its most general form, which is called a "searched case" in the SQL standard, it works like else if in other programming languages:

 CASE WHEN n > 0 THEN 'positive' WHEN n < 0 THEN 'negative' ELSE 'zero' END 

The WHEN conditions are tested in the order in which they appear in the source. If no ELSE expression is specified, it defaults to ELSE NULL. An abbreviated syntax exists mirroring switch expressions; it is called "simple case" in the SQL standard:

 CASE n WHEN 1 THEN 'one' WHEN 2 THEN 'two' ELSE 'i cannot count that high' END 

This syntax uses implicit equality comparisons, with the usual caveats for comparing with NULL.

For the Orcacle-SQL dialect, the latter can be shortened to an equivalent DECODE construct:

 SELECT DECODE(n, 1, "one", 2, "two", "i cannot count that high") FROM some_table; 

The last value is the default; if none is specified, it also defaults to NULL. However, unlike the standard's "simple case", Oracle's DECODE considers two NULLs to be equal with each other.[4] Technically, DECODE is just a PL/SQL function from Oracle's STANDARD PL/SQL package (no relation to the SQL standard), but DECODE is restricted to being invoked only from SQL code.[5]

Visual Basic .NET

In Visual Basic .NET, the switch statement is called "Select Case", and fall-through to later blocks is not supported. However, ranges and various constructs from If statements are both supported

 Select Case n   Case Is < -5     MsgBox("n is less than -5")   Case -4 To -1     MsgBox("n is between -4 and -1")   Case 0     MsgBox("n is 0")   Case 2, 4, 6, 8     MsgBox("n is even")   Case 1, 3, 5, 7, 9     MsgBox("n is odd")   Case Else     MsgBox("only single-digit numbers are allowed.", vbCritical) End Select 

Visual FoxPro

Visual FoxPro:

 Do Case Case field_1 = "X"    Replace field_b With 1 Case field_1 = "Y"    Replace field_b With 2 Case field_1 = "Z"    Replace field_b With 3 Otherwise    Replace field_b with 0 Endcase 

Visual Basic (classic), VBA, VB Script

In Visual Basic, the switch statement is called "Select Case", and fall-through to later blocks is not supported. Short-circuit evaluation is used. But also, may doing exactly like C, using GOSUB behind Select Case. the block Select Case then call to GOSUB label, where each BREAK in C it's a RETURN (to gosub).

 Select Case n   Case "s"     MsgBox "Case values of any type are supported"   Case "s"     MsgBox "This block is not an error but will never be selected"   Case ArbitraryFunction()     MsgBox "Any Expression which can be evaluated at runtime may be used as a case value or selector"   Case 2+3: MsgBox "The colon is a general language feature, not a part of the switch statement"             MsgBox "Each block is terminated by the following Case or End statement"   Case Else     MsgBox "Case values which do not match the selector type will cause an exception", vbCritical End Select 

WebDNA

The WebDNA example is easy to understand:

 [text]x=5[/text]   [switch value=[x]]   [case value=1]     The value of x was 1   [/case]   [case value=2]     The value of x was 2   [/case]   [default]     The value of x was neither 1 nor 2; it was [x]   [/default] [/switch] 

Windows PowerShell

Windows PowerShell employs a construct whereby the action to be taken is enclosed in a scriptblock (i.e. curly braces), with the selector placed directly before it. The selector can consist of regular expressions if the "-regex" parameter is inserted after the "switch" command; similarly, wildcards are supported using the "-wildcard" parameter. In either case, the wildcard or regex must be enclosed in quote marks.[6]

Akin to C-based implementations, if a "break" statement is not included at the end of a scriptblock, the switch statement will continue to test each case and execute further scriptblocks.

 switch (n) {   0 { Write-Host 'You typed 0' }   { ($_ -eq 1) -or ($_ -eq 4) -or ($_ -eq 9) }     { Write-Host 'n is a perfect square' }   { (($_ % 2) -eq 0) -and ($_ -ne 0) }     { Write-Host 'n is an even number' }   { ($_ -eq 2) -or ($_ -eq 3) -or ($_ -eq 5) -or ($_ -eq 7) }     { Write-Host 'n is an prime number' }   default { Write-Host 'Only single-digit numbers are allowed' } } 

Symbolic constants

In many (but not all) circumstances, using symbolic names rather than explicit literal integers makes the source code easier to read and maintain. This is often achieved via enumerations and has no influence on the performance or behavior of the program. This style of switch statement is commonly used for finite state machine implementation. Here are some examples, given in Pascal (one of the first languages implementing enumerations, along with Algol68) as well as in C (which has a tradition of constants in all capitals, although not enforced by the compiler.)

Pascal (using an enumeration)

 var state : (StateReady, StateSet, StateGo, StateFail);   case state of     StateReady: begin         state:=succ(state);         if x < 0 then state:=StateFail     end;     StateSet: begin         state:=succ(state);         if y > 0 then state:=StateFail     end;       StateGo:   writeln("go!");     StateFail: exit( -1 ); end; 

C (using the enum keyword)

 enum state {    STATE_READY = 1,    STATE_SET = 2,    STATE_GO = 3,    STATE_FAIL = 4 };   switch( state ) {    case STATE_READY:        state = STATE_SET;        if( x < 0 ) state = STATE_FAIL;        break;      case STATE_SET:        state = STATE_GO;        if( y > 0 ) state = STATE_FAIL;        break;      case STATE_GO:        printf( "go!\n" );        break;      case STATE_FAIL:        exit( -1 ); } 

Alternative uses

Many languages evaluate expressions inside switch blocks at runtime, allowing a number of less obvious uses for the construction. This prohibits certain compiler optimizations, so is more common in dynamic and scripting languages where the enhanced flexibility is more important than the performance overhead.

For example, in PHP, a constant can be used as the "variable" to check against, and the first case statement which evaluates to that constant will be executed:

 switch(true) {   case ($x == 'hello'):     foo();      break;   case ($z == 'howdy'): break; } switch(5) {   case $x: break;   case $y: break; } 

This feature is also useful for checking multiple variables against one value rather than one variable against many values. COBOL also supports this form (and others forms) in the EVALUATE statement.

In Ruby, due to its handling of === equality, the statement can be used to test for variable’s class:

 case input when Array: puts 'input is an Array!' when Hash:  puts 'input is a Hash!' end 

Ruby also returns a value that can be assigned to a variable, and doesn’t actually require the case to have any parameters (acting a bit like an else if statement):

 catfood = case           when cat.age <= 1: junior           when cat.age > 10: senior           else               normal           end 

Alternatives

(In some languages, only actual data types are allowed as values in the lookup table. In other languages, it is also possible to assign functions as lookup table values, gaining the same flexibility as a real switch statement. See Control table article for more detail on this).
Lua does now support Case/Switch statements: http://lua-users.org/wiki/SwitchStatement . This lookup technique is one way to implement switch statements in the Lua language - which has no built-in switch.[7]
In some cases, lookup tables are more efficient than non-optimized switch statements since many languages can optimize table lookups - whereas switch statements are not optimized unless the range of values is small with few gaps. A non-optimized, non-binary search lookup, however, will almost certainly be slower than either a non-optimized switch or the equivalent multiple if-else statements.[citation needed]

See also

References

External links