Embarcadero Prism - Learning the Oxygene language continued

By: Tim DelChiaro

Abstract: Learn about language control structures including begin, end, if, case, boolean expressions, and loops

This information comes from The Oxygene Language Wiki. See the links at the bottom of the article to navigate to additional content on the wiki and view any updates to this information.


    Control Structures

Control structures are a very important part of every programming language. They control the flow of your program and make it possible to execute one part of code more than once or "decide" between different parts of code.

begin and end

Before we can talk about such structures, it has to be clear what "parts of code" are. How does one define a part? Well, that's relatively simple: You define its beginning using the begin keyword and its end with the end keyword. You've already seen both, e.g. in the code that's executed when the button is clicked. Everything that is executed is one "block", which is the correct word for "part of code".

Why is there no semicolon after begin? Because begin isn't a command, but marks the start of a command! The end of the command is the endkeyword and there's your semicolon then ;-) That's important: The begin-end-block is one command in Delphi Prism!

ifcase and Boolean Expressions

We want to extend the mini calculator by adding another feature: dividing numbers. Add a new button "btDivide" with the text "Divide" to the form. Double click on it to get to the event handler. The code is almost the same as for the addition. Copy it into the new event handler and replace the "+" by "/". This is the division operator. This is an integer division, so 4 / 3 is 1 and not 1.33.

DivideByZeroException

Run the program, input "5" as first number and "0" as second number. When clicking "Divide" the program will throw an exception. Those are thrown every time the program does something that shouldn't be done and the program can't continue without handling this exception. Most of these "An unexpected error occured" messages you get by some programs are unhandled exceptions. This time you tried to divide by zero which isn't defined.

The if Statement

The best way to deal with exceptions is to make sure they don't occur. So we have to check if the second number is zero and if it is, we'll show a "nice" error messgae in the result textbox.

This looks (for the moment) like this:

var firstNumber := Integer.Parse(tbFirstNumber.Text);
var secondNumber := Integer.Parse(tbSecondNumber.Text);

if secondNumber = 0 then
  tbResult.Text := 'Division by Zero';

if secondNumber <> 0 then
begin
  var resultNumber := firstNumber / secondNumber;
  tbResult.Text := resultNumber.ToString; 
end;

You know the first two lines, but the next line is something new, it contains the 'if' statement. Its structure is:

 if {condition} then
   {command}

So, in the first 'if' statement, the condition is that secondNumber equals zero. The command is to display "Divide by Zero" in the result textbox. By the way, this is one of defining strings in your source code: Start it with an apostrophe, end it the same way and put your text in between.

The second 'if' statement is similar. The condition is that the second number is not zero. The command is a begin-end-block which, as we learned earlier, is one command.

In the next section we'll take a look at what conditions can look like.

In this section there's another keyword to introduce: else. We can write the previous code a little bit shorter and much better to read using this keyword:

if secondNumber = 0 then
  tbResult.Text := 'Division by Zero'
else begin
  var resultNumber := firstNumber / secondNumber;
  tbResult.Text := resultNumber.ToString; 
end;

This source code does exactly the same thing as the previous source code, because the first condition is the exact negation of the second condition. The first branch ("if-branch") is only executed if the condition is true, otherwise the second branch ("else-branch") is executed.

Perhaps you'll notice that there's no semicolon behind the line in the if-branch. That's no mistake and it's no exception to the rule that every command has to be separated by a semicolon. The command we're dealing with here is the if-command and this is not finished there: the else-branch is part of the if-command, too, so the semicolon has to be placed after the else-branch.

Of course you can nest if-commands into each other:

 if {condition} then
   if {condition} then
     {command};

There's no problem here, but it can get unclear if you add an else-branch:

 if {condition} then
   if {condition} then
     {command}
 else
   {command};

That's wrong! The indentation suggests that the else-branch belongs to the first if-statement, but in fact it does belong to the second if-statement. The else-branch always belongs the next inner if-branch. Therefore, the correct indentation is:

 if {condition} then
   if {condition} then
     {command}
   else
     {command};

If you want to have an else-branch for the first if-statement, you have to put the second if-statement into an begin-else-block.

 if {condition} then
 begin
   if {condition} then
     {command};
 end
 else
   {command};

Note that the position of the semicolons has changed now, too.

Boolean Expressions

The condition of an if-statement is a boolean value. A boolean value can be true or false. .NET offers the variable type Boolean for that.

The check for equality of two numbers has a boolean result and therefore can be used as condition in an if-statement. Other comparisons could be "greater than" (>) or "less than" (<). The statement "I have more money in my purse than you" can be only true or false. I can define the amount of money in my purse as "number1" and the amount of money in your purse as "number2". The statement can be then written as number1 > number2.

Boolean expression (also called boolean operations, named after George Boole) are the composition of boolean values and have a boolean value as result. You can use them to define more complex conditions:

newBoolean := boolean1 and boolean2;
newBoolean := boolean1 or boolean2;
newBoolean := not boolean1;
newBoolean := boolean1 xor boolean2;

The first two expressions should be self-explanatory. In the first expression newBoolean is only true if boolean1 is true and boolean2 is true. In the second expression newBoolean is only true if boolean1 is true or boolean2 is true or both are true.

The third one is the negation, newBoolean is true if boolean1 is and vice versa.

The fourth expression is called "exclusive or". In colloquial language you'd call it "either ... or". newBoolean is true if either boolean1 or boolean2 is true, but not both!

Because every of these expressions results in a boolean, you can create new compositions with them:

boolean1 and boolean2 or boolean3

This source code is valid and can be evaluated unambiguously, but it's not good to read. You have to think about which part of the expression will be evaluated first. I recommend using braces even if they are not needed:

(boolean1 and boolean2) or boolean3

Delphi Prism would've evaluated the expression like that in the first place. It uses something called operator precedence, which you can imagine as the "strength of binding" of an operator. Or a little bit easier: How "important" the operator is ;-). You've learned in school that multiplication and division have a higher precedence than addition and subtraction.

The precedence of boolean operators is:

* not
* and
* or
* xor

Operators of same precedence are evaluated from the left to the right.

You can make use of this fact: Delphi Prism evaluates a boolean expression only until its value is known. The rest of the expression won't be evaluated if it can't change the value of the expression anymore. For example if you have an expression A and B and A is already false, B won't be taken into account, because the complete expression must be false! This is called short-circuit evaluation.

So, how can you make use of this? Write the expressions that can be evaluated fastest to the left. If A and B are both functions, then you would choose Ato be the one that is evaluated fast and B the slow one. If the "fast" method is false, the "slow" method won't be evaluated and the whole expression is evaluated faster.

But you have to make sure that the method potentially not evaluated doesn't have side effects. If this method must be evaluated, because it does more than only return a boolean value, you shouldn't use it directly in a boolean expression but evaluate it seperately and store its result in a variable which you then use in the expression.

The case Statement

In some cases, there's a better way to bring branches into your program than using an if expression. Let's take a look at this source code:

if number1 = 0 then
  command1;
if number1 = 1 then
  command2;
if number1 = 2 then
  command1;
...

That doesn't look very nice. The case command will make it look much better:

case number1 of
  0: command1;
  1: command2;
  2: command1;
  ...
end;

I think it's clear how this works. It's like a table of values and command: you look up the value of number1 in the table to find the appropriate command and then execute it.

The above code can be written even shorter:

case number1 of
  0,2 : command1;
  1: command2;
  ...
end;

If there's a default command you want to have executed if none of the values match, the case command offers an else branch:

case number1 of
  0,2 : command1;
  1: command2;
else
  defaultCommand;
end;

The case command works with every type you like, for example strings.

Loops

Loops allow the programmer to repeatedly execute parts of code. The number of times the code is executed needn't be known when the program is written, but can vary during runtime. There are several ways to terminate a loop, like giving a specific number of iterations or a boolean "termination condition". There's more to know about loops in Delphi Prism than we will see in this chapter, but it's enough for the start.

The For Loop

To demonstrate the 'for' loop, we will extend the mini calculator with a new feature: Calculation of a number's factorial. For example the factorial of 5 is written as 5! and can be calculated as 5*4*3*2*1. It's often used in probability calculations.

Please add a button "btFactorial" to the mini calculator and then switch to its click event by double-clicking on it. This time we'll only need firstNumber, because we only want the factorial of the first number in the textbox. In addition the resultNumber has to be initialized with the value 1:

var firstNumber := Integer.Parse(tbFirstNumber.Text);
var resultNumber := 1;

Now we want to build the loop that calculates the factorial, which means that resultNumber has to be multiplied first by 2, then by 3 and so on until we reach firstNumber. That looks like this:

for i : Integer := 2 to firstNumber do
  resultNumber := resultNumber * i;

Let's analyze what we see here. First thing is the i : Integer. This is the loop counter or loop variable. A for loop in Delphi Prism always defines an own variable for this and never uses a variable declared outside the loop.

Its value will change from itertation to iteration. The range of values is defined by the second part of the for loop's header: 2 to firstNumber. For each number the body (resultNumber := ...) of the loop will be executed with i having the according value.

What's happing in the loop gets much clearer, if we "step through it". Before the body is executed the first time, the resultNumber is 1. Then a new value is assigned to resultNumber: the product of resultNumber's old value (= 1) and the first value of i (= 2). Therefore the new value of resultNumber is 2.

You can write down the steps of the loop like this:

resultNumber :=  1 * 2; //resultNumber is 1,        i is 2
resultNumber :=  2 * 3; //resultNumber is 1*2 =  2, i is 3
resultNumber :=  6 * 4; //resultNumber is 2*3 =  6, i is 4
resultNumber := 24 * 5; //resultNumber is 6*4 = 24, i is 5

By the way: The gray parts in the source code are comments, which will be ignored by the Delphi Prism compiler and are only notes for the programmer.

The loop in this sample does only count up and only by one per iteration. You can modify both. By replacing the to with downto the loop will count down. Of course the loop variable's initial value has to be greater than the end value, otherwise the loop wouldn't ever execute its body.

To change the value by which the loop variable is incremented or decremented per iteration, you can specify a step size:

for i : Integer := 10 downto 2 step 2 do

In this case i will have the values 10,8,6,4,2.

(To complete the extension of the calculator, you'll have to add an additional line to the method to display the result. You should know how that has to look. ;-))

The Repeat-Until Loop

In many cases you can't predetermine how often the loop body has to be executed and therefore you can't use a 'for' loop. In that case you can use a loop with a boolean termination condition. For example a 'repeat-until' loop.

The name of the loop explains pretty well what it does. It repeats executing the body until a condition (a boolean expression) becomes true. The calculation of the factorial can be written like this:

var firstNumber := Integer.Parse(tbFirstNumber.Text);
var resultNumber := 1;
var i := 2;

repeat
  resultNumber := resultNumber * i;
  i := i + 1;
until i > firstNumber;

As you can see, we introduced a variable that we use as loop variable and increment manually in every iteration. You are not bound to a loop variable in repeat-until loops, it's just in this sample. The body is executed until i is greater than firstNumber.

An important thing about the repeat-until loop is that the body is executed at least once, because the condition is checked after the body was executed the first time ("post-test loop"). Also note, that you don't need begin and end to have a body with more than one line, the body is delimited by repeatand until.

The While-Do Loop

The 'while-do' loop is very similar to the repeat-until loop. The previous source code looks like this when using a while-do loop:

var firstNumber := Integer.Parse(tbFirstNumber.Text);
var resultNumber := 1;
var i := 2;

while i <= firstNumber do
begin
  resultNumber := resultNumber * i;
  i := i + 1;
end;

The differences between while-do and repeat-until are:

  • The condition of a while-do loop is checked before the body is executed ("pre-test loop"). Therefore it's possible that the body is never executed.
  • A body with more than one line has to use a begin-end block.
  • The body is executed while the condition is true. The loop cancels when the condition is false (which is the exact opposite of the repeat-until loop's behaviour).
Controlling Loops

It happens that you want to completely cancel a loop or just skip the current step of a loop and directly continue with the next one. In the first case, this can be done with the break command, in the second case with the continue command.

For example:

for i : Integer := 0 to 10 do
begin
  if i = 5 then
    continue;

  if i = 8 then
    break;

  MessageBox.Show(i.ToString);
end;

The first if-command will make the loop skip the rest of the method body and directly continue with i = 6, if i equals five. The second if-command will cancel the loop completely, if i equals eight. The loop will therefore display: 0,1,2,3,4,6,7.

The Loop Statement

Delphi Prism offers another kind of loop: A loop command, which in fact is an infinite loop which only stops if you call break. You can use it like this:

var i := -1;
loop begin
  i := i + 1;

  if i = 5 then
    continue;

  if i = 8 then
    break;

  if i = 10 then
    break;

  MessageBox.Show(i.ToString);
end;

Of course the third if-statement is useless, but needed for this loop to be equivalent to the for-loop above.

    See Also

The Prism PrimerPart 1 – Part 2 – Part 3 – Part 4 – Part 5 – Part 6 – Part 7 – Part 8 – Part 9



Server Response from: ETNASC02