Switch statement

From Wikipedia, the free encyclopedia

Jump to: navigation, search

Contents

In computer programming, a switch statement is a type of control statement that exists in most modern imperative programming languages (e.g., Pascal, C, C++, C#, and Java). Its purpose is to allow the value of a variable or expression to control the flow of program execution. In some other programming languages, a statement that is syntactically different but conceptually the same as the switch statement is known as a case statement, select statement or inspect instruction.

In most languages, a switch statement is defined across many individual statements. A typical syntax is that the first line contains the actual word "switch" followed by either the name of a variable or some other expression allowed by the language's syntax. This variable or expression is usually referred to as the "control variable" of the switch statement. After this line, following lines define one or more blocks of code that represent possible branches that program execution may take.

Each block begins with a line containing the case keyword followed by a value that the control variable may have. If the value of the control variable matches this value, program execution will jump to that block of code. If not, the value specified in the next block (if present) is examined and the process repeats.

An optional special block is also allowed, which does not specify any value and which begins with the default keyword instead of the case keyword. If this block is present and if none of the values listed for any other block matches that of the control variable, program execution will jump to the statement following the default keyword.

The method of terminating a block is also of note. Typically, a break keyword is used to signal the end of the block. When encountered, this keyword causes program execution to continue with the first statement after the series of statements within the switch statement, thus completing execution of the switch statement. If no break keyword is present at the end of the block, in many languages program execution "falls through" to the code associated with the next block in the switch statement, as if its value also matched the value of the control variable. Notable exceptions include C#, in which fallthrough is not permitted unless the block is empty and all blocks must be terminated via a break, or by using another keyword. Similarly, almost all BASIC dialects that feature this type of statement do not allow fallthrough.

[edit] 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 - which can be very much faster.

[edit] Optimized switch

It is probably true to say however that many programmers do not realize how extremely important it is to obtain a very compact range of possible values for optimization to actually occur within the switch statement. They assume, incorrectly, that simply using a switch statement in their program is inherently efficient. It may indeed be true that the code is much neater and easier to maintain - but it is certainly not necessarily always optimized for faster execution unless the values are strictly suitable. Sometimes they have to be converted first by the programmer to a more suitable range using a simple transformation (complex/lengthy transformations may defeat the purpose so should be avoided). 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.

[edit] Checking for optimization

Normally, the only method of finding out if this optimization has occurred is by actually looking at the resultant assembler 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' thru '9' with zero gaps without a defined case label).

[edit] Advantages and disadvantages

In some languages and programming environments, a case or switch statement is considered

  • Easier debuggability (e.g. setting breakpoints on code vs. a call table)
  • easier to read (subjective) and
  • easier to maintain than an equivalent series of if-else statements

Additionally, as mentioned above, an optimized implementation may:

  • execute much faster than an equivalent series of if-else statements, often implemented by using an indexed branch table

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

However, 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).

[edit] Examples

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

[edit] C, C++, C#, Java

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. For example, if n=5, the third case statement will produce a match to the control variable. Since there are no statements following this line and no break keyword, execution continues through the 'case 7:' line and to the next line, which produces output. The break line after this causes the switch statement to conclude. If the user types in more than one digit, the default block is executed, producing an error message.

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

[edit] Pascal

 case place of
   1: writeln('Champion');
   2: writeln('First runner-up');
   3: writeln('Second runner-up'); 
   else writeln('Work hard next time!'); 
 end;

[edit] Python

Python does not have direct language support for the switch statement. 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; a function per case is needed, and hashing is used to determine which function is called. The default case is mimicked by using dict.get()'s fallback parameter:

switch = {
    0 : TypedZeroFunc,
    1 : PerfectSquareFunc,
    2 : PrimeNumberFunc,
    3 : PrimeNumberFunc,
    4 : PerfectSquareFunc
}
 
switch.get(n, DefaultFunc)() # Switch based on value of n.

[edit] Ruby

Ruby doesn’t have the “fall through” mechanism; it also uses case instead of switch, when instead of case and else instead of default.

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

[edit] Ada

Ada doesn’t have the “fall through” mechanism; it also uses case instead of switch, when instead of case and others instead of default. In addition, 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 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 6 | 8 =>
         Put_Line ("n is an even number");
   end case;

[edit] Eiffel

Eiffel's multi-branch instruction uses inspect, when, and else versus the switch, case, and default of C. It does not have “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

[edit] 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!"

[edit] Bash

Bash and other 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.

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.";;
    2|4)    echo "$n is a perfect square.
$n is an even number";;
    6|8)    echo "$n is an even number.";;
    *)      echo 'Only single-digit numbers are allowed.';
esac

[edit] 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.[1]

Unlike 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' }
}

[edit] Visual Basic

In Visual Basic, the switch statement is called "Select Case", and fall-through to later blocks is not supported.

Select Case n
  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 positive single-digit numbers are allowed.", vbCritical
End Select

[edit] Symbolic constants

In many (but not all) circumstances, using names rather than ordinary integers makes the source code easier to read. This has no influence on the behavior of the program. This style of switch statement is commonly used for finite state machine implementation. The tradition in C is for such constants to be in all capitals, although this is not enforced by the compiler. Here are some examples:

[edit] C (using enum)

enum
{
   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 );
}

[edit] Alternative uses

Many languages also support using "true" as the variable, and having an expression as the case.

For example in PHP you can do:

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

The reason this works is because the variable being switched for is the boolean true. So having x=='hello' as a condition would either return true or false, so if that is true, it matches the thing which is being checked.

You could also use this for checking multiple variables against one value instead of checking multiple values for one variable:

//example 1: common use
switch($x) {
  case 5: break;
  case 6: break;
}
//example 2: alternative use
switch(5) {
  case $x: break;
  case $y: break;
}

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


[edit] Alternatives

One alternative to a switch statement can be the use of a lookup table which contains as keys the case values and as values the part under the case statement. 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 (this is one way to implement switch statements in Lua which has no built-in switch [2]).

In some cases, lookup tables are more efficient than switch statements as many languages can optimize the table lookup whereas switch statements are often not optimized that much[citation needed]. 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).

Another "alternative" to switch statements is the extensive use of polymorphism.

[edit] References

  1. ^ "Windows PowerShell Tip of the Week". Microsoft. January 2008. http://www.microsoft.com/technet/scriptcenter/resources/pstips/jan08/pstip0111.mspx. Retrieved on 2009-04-28. 
  2. ^ Switch statement in Lua

[edit] See also

[edit] External links

Optimizing Switch-Case Statements In C For Speed

Personal tools