Jump to content

Switch statement

From Wikipedia, the free encyclopedia

This is an old revision of this page, as edited by 71.230.242.112 (talk) at 23:28, 24 September 2010 (→‎Optimized switch). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

In computer programming, a switch statement is a type of selection 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 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), offering the potential of faster execution through compiler optimization. In some 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.

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 statements. A typical syntax is:-

  • First line contains the actual word "switch" followed by either the name of a variable or an expression if allowed by the language's syntax. This variable or expression is usually referred to as the "control variable" of the switch statement.
  • subsequent lines define one or more blocks of code that represent possible branches that program execution may take.

Each alternative block of code 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 the beginning of that block of code (which is simply a program label within the current procedure). 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 (i.e. goto) 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. It is equivalent to a 'goto the end of the series of case statement blocks'. 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. Perl is an interesting "inverted" case in that its cases do not fall through by default, but one can explicitly have it fall through with a "continue" statement.

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.

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:

  • easier to debug (e.g. setting breakpoints on code vs. a call table, if the debugger has no conditional breakpoint capability)
  • easier to read (subjective)
  • easier to understand and therefore
  • easier to maintain

Additionally, as mentioned above, an optimized implementation may:

  • execute much faster than the alternative, because it is often implemented by using an indexed branch table
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.

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.

C, C++, 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. 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:
    puts("You typed zero.");
    break;
  case 1:
  case 4:
  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 6:
  case 8:
    puts("n is an even number.");
    break;
  default:
    puts("Only single-digit numbers are allowed.");
    break;
}

C#

In C#, every case block that contains any statements must have an unreachable 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.[1] 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.

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;
    goto case 6;
  case 6:
  case 8:
    Console.WriteLine("n is an even number.");
    break;
  default:
    Console.WriteLine("Only single-digit numbers are allowed.");
    break;
}

Go

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 a expression "switch" statement.[2]

Pascal

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

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

Perl

Perl 5.10 (backported from Perl 6) has a powerful builtin 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";
    }
}

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)()

# A temporary variable is not needed.
answer = {
    0: "Zero",
    1: "Perfect square",
    2: "Prime",
    3: "Prime",
    4: "Perfect square",
    }.get(n, "Unknown")

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 
  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

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 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 6 | 8 =>
         Put_Line ("n is an even number");
   end case;

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

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!"

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

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.[3]

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

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

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 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

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]

Visual Foxpro

Visual FoxPro:

Do Case
 Case field_1 = "Z"
  Replace field_b With 1
 Case field_1 = "Y"
  Replace field_b With 2
 Case field_1 = "Z"
  Replace field_b With 3
Endcase

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.

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. Java and COBOL have equivalent capabilities. Here are some examples:

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

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

  • A series of nested if-else conditionals that examine the target one value at a time.
  • 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. See Control table article for more detail on this).
This lookup technique is one way to implement switch statements in the Lua language - which has no built-in switch [4].
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.
  • A control table (that may be implented as a simple lookup table) can also be customized to accommodate multiple conditions on multiple inputs if required and usually exhibits greater 'visual compactness' than an equivalent switch (that can occupy many statements).
  • For object-oriented programs, extensive use of polymorphism can be used

See also

References

  1. ^ switch (C# Reference)MSDN Library
  2. ^ "The Go Programming Language Specification". Retrieved 2010-01-18.
  3. ^ "Windows PowerShell Tip of the Week". Microsoft. January 2008. Retrieved 2009-04-28.
  4. ^ Switch statement in Lua
  • Stephen Kleene, 1952 (10th reprint 1991), Introduction to Metamathematics, North-Holland Publishing Company, Amsterdam NY, ISBN 0 7204 2103 9
  • George Boolos, John Burgess, and Richard Jeffrey, 2002, Computability and Logic: Fourth Edition, Cambridge University Press, Cambridge UK, ISBN 0521 00758 5 paperback. cf page 74-75.