Previous chapter To contents Next chapter

Chapter 3, Control Structures

In this chapter all the control structures in Pike will be explained. As mentioned earlier, control structures are used to control the flow of the program execution. Note that functions that make the program pause and simple function calls are not qualified as control structures.

3.1 Conditions

Pike only has two major condition control structures. We have already seen examples of both of them in Chapter two. But for completeness they will be described again in this chapter.

3.1.1 if

The simplest one is called the if statement. It can be written anywhere where a statement is expected and it looks like this:
if( expression ) statement1; else statement2;
Please note that there is no semicolon after the parenthesis or after the else. Step by step, if does the following:
  1. First it evaluates expression.
  2. If the result was false go to point 5.
  3. Execute statement1.
  4. Jump to point 6.
  5. Execute statement2.
  6. Done.
This is actually more or less how the interpreter executes the if statement. In short, statement1 is executed if expression is true otherwise statement2 is executed. If you are interested in having something executed if the expression is false you can drop the whole else part like this:
if( expression )
    statement1;
If on the other hand you are not interested in evaluating something if the expression is false you should use the not operator to negate the true/false value of the expression. See chapter 5 for more information about the not operator. It would look like this:
if( ! expression )
    statement2 ;
Any of the statements here and in the rest of this chapter can also be a block of statements. A block is a list of statements, separated by semicolons and enclosed by brackets. Note that you should never put a semicolon after a block of statements. The example above would look like this;
if ( ! expression )
{
    statement;
    statement;
    statement;
}

3.1.2 switch

A more sophisticated condition control structure is the switch statement. A switch lets you select one of many choices depending on the value of an expression and it can look something like this:
switch ( expression )
{
    case constant1:
        statement1;
        break;

    case constant2:
        statement2;
        break;

    case constant3 .. constant4:
        statement3;
        break;

    default:
        statement5;
}
As you can see, a switch statement is a bit more complicated than an if statement. It is still fairly simple however. It starts by evaluating the expression it then searches all the case statements in the following block. If one is found to be equal to the value returned by the expression, Pike will continue executing the code directly following that case statement. When a break is encountered Pike will skip the rest of the code in the switch block and continue executing after the block. Note that it is not strictly necessary to have a break before the next case statement. If there is no break before the next case statement Pike will simply continue executing and execute the code after that case statement as well.

One of the case statements in the above example differs in that it is a range. In this case, any value between constant3 and constant4 will cause Pike to jump to statement3. Note that the ranges are inclusive, so the values constant3 and constant4 are also valid.

3.2 Loops

Loops are used to execute a piece of code more than once. Since this can be done in quite a few different ways there are four different loop control structures. They may all seem very similar, but using the right one at the right time makes the code a lot shorter and simpler.

3.2.1 while

While is the simplest of the loop control structures. It looks just like an if statement without the else part:
while ( expression )
    statement;
The difference in how it works isn't that big either, the statement is executed if the expression is true. Then the expression is evaluated again, and if it is true the statement is executed again. Then it evaluates the expression again and so forth... Here is an example of how it could be used:
int e=1;
while(e<5)
{
    show_record(e);
    e=e+1;
}
This would call show_record with the values 1, 2, 3 and 4.

3.2.2 for

For is simply an extension of while. It provides an even shorter and more compact way of writing loops. The syntax looks like this:
for ( initializer_statement ; expression ; incrementor_expression )
    statement ;
For does the following steps:
  1. Executes the the initializer_statement. The initializer statement is executed only once and is most commonly used to initialize the loop variable.
  2. Evaluates expression
  3. If the result was false it exits the loop and continues with the program after the loop.
  4. Executes statement.
  5. Executes the increment_expression.
  6. Starts over from 2.
This means that the example in the while section can be written like this:
for(int e=1; e<5; e=e+1)
    show_record(e);

3.2.3 do-while

Sometimes it is unpractical that the expression is always evaluated before the first time the loop is executed. Quite often you want to execute something, and then do it over and over until some condition is satisfied. This is exactly when you should use the do-while statement.
do
    statement;
while ( expression );
As usual, the statement can also be a block of statements, and then you do not need a semicolon after it. To clarify, this statement executes statement first, and then evaluates the expression. If the expression is true it executes the loop again. For instance, if you want to make a program that lets your modem dial your Internet provider, it could look something like this:
do {
    modem->write("ATDT441-9109\n"); // Dial 441-9109
} while(modem->gets()[..6]] != "CONNECT");
This example assumes you have written something that can communicate with the modem by using the functions write and gets.

3.2.4 foreach

Foreach is unique in that it does not have an explicit test expression evaluated for each iteration in the loop. Instead, foreach executes the statement once for each element in an array. Foreach looks like this:
foreach ( array_expression, variable )
    statement ;
We have already seen an example of foreach in the find_song function in chapter 2. What foreach does is:
  1. It evaluates the array_expression which must return an array.
  2. If the array is empty, exit the loop.
  3. It then assigns the first element from the array to the variable.
  4. Then it executes the statement.
  5. If there are more elements in the array, the next one is assigned to the variable, otherwise exit the loop.
  6. Go to point 4.
Foreach is not really necessary, but it is faster and clearer than doing the same thing with a for loop, as shown here:
array tmp1= array_expression;
for ( tmp2 = 0; tmp2 < sizeof(tmp1); tmp2++ )
{
    variable = tmp1 [ tmp2 ];
    statement;
}

3.3 Breaking out of loops

The loop control structures above are enough to solve any problem, but they are not enough to provide an easy solution to all problems. One thing that is still missing is the ability to exit a loop in the middle of it. There are three ways to do this:

3.3.1 break

break exits a loop or switch statement immediately and continues executing after the loop. Break can not be used outside of a loop or switch. It is quite useful in conjunction with while(1) to construct command parsing loops for instance:
while(1)
{
    string command=Stdio.Readline()->read("> ");
    if(command=="quit") break;
    do_command(command);
}

3.3.2 continue

Continue does almost the same thing as break, except instead of breaking out of the loop it only breaks out of the loop body. It then continues to execute the next iteration in the loop. For a while loop, this means it jumps up to the top again. For a for loop, it jumps to the incrementor expression. For a do-while loop it jumps down to the expression at the end. To continue our example above, continue can be used like this:
while(1)
{
    string command=Stdio.Readline()->read("> ");
    if(strlen(command) == 0) continue;
    if(command=="quit") break;
    do_command(command);
}
This way, do_command will never be called with an empty string as argument.

3.3.3 return

Return doesn't just exit the loop, it exits the whole function. We have seen several examples how to use it chapter 2. None of the functions in chapter two returned anything in particular however. To do that you just put the return value right after return. Of course the type of the return value must match the type in the function declaration. If your function declaration is int main() the value after return must be an int. For instance, if we wanted to make a program that always returns an error code to the system, just like the UNIX command false this is how it would be done:
#!/usr/local/bin/pike

int main()
{
    return 1;
}
This would return the error code 1 to the system when the program is run.

3.4 Exercises

  • End all functions in the examples in chapter two with a return statement.
  • Change all foreach loops to for or while loops.
  • Make the find_song function in chapter 2 return when the first matching song is found.
  • Make the find_song function write the number of the record the song is on.
  • If you failed to get the program to work properly in the last exercise of chapter 2, try it again now.
  • Make a program that writes all the numbers from 1 to 1000.
  • Modify the program in the previous exercise to NOT write numbers divisible by 3, 7 or 17.
  • Make a program that writes all the prime numbers between 1 and 1000.

  • Previous chapter To contents Next chapter