Programming in D – Tutorial and Reference
Ali Çehreli

Other D Resources

if Statement

We've learned that the actual work in a program is performed by expressions. All of the expressions of all of the programs that we've seen so far have started with the main() function and were executed until the end of main.

Statements, on the other hand, are features that affect the execution of expressions. Statements don't produce values and don't have side effects themselves. They determine whether and in what order the expressions are executed. Statements sometimes use logical expressions when making such decisions.

Note: Other programming languages may have different definitions for expression and statement, while some others may not have a distinction at all.

The if block and its scope

The if statement determines whether one or more expressions would be executed. It makes this decision by evaluating a logical expression. It has the same meaning as the English word "if", as in the phrase "if there is coffee then I will drink coffee".

if takes a logical expression in parentheses. If the value of that logical expression is true, then it executes the expressions that are within the following curly brackets. Conversely, if the logical expression is false, it does not execute the expressions within the curly brackets.

The area within the curly brackets is called a scope and all of the code that is in that scope is called a block of code.

Here is the syntax of the if statement:

    if (a_logical_expression) {
        // ... expression(s) to execute if true
    }

For example, the program construct that represents "if there is coffee then drink coffee and wash the cup" can be written as in the following program:

import std.stdio;

void main() {
    bool existsCoffee = true;

    if (existsCoffee) {
        writeln("Drink coffee");
        writeln("Wash the cup");
    }
}

If the value of existsCoffee is false, then the expressions that are within the block would be skipped and the program would not print anything.

The else block and its scope

Sometimes there are operations to execute for when the logical expression of the if statement is false. For example, there is always an operation to execute in a decision like "if there is coffee I will drink coffee, else I will drink tea".

The operations to execute in the false case are placed in a scope after the else keyword:

    if (a_logical_expression) {
        // ... expression(s) to execute if true

    } else {
        // ... expression(s) to execute if false
    }

For example, under the assumption that there is always tea:

    if (existsCoffee) {
        writeln("Drink coffee");

    } else {
        writeln("Drink tea");
    }

In that example, either the first or the second string would be printed depending on the value of existsCoffee.

else itself is not a statement but an optional clause of the if statement; it cannot be used alone.

Note the placement of curly brackets of the if and else blocks above. Although it is official D style to place curly brackets on separate lines, this book uses a common style of inline curly brackets throughout.

Always use the scope curly brackets

It is not recommended but is actually possible to omit the curly brackets if there is only one statement within a scope. As both the if and the else scopes have just one statement above, that code can also be written as the following:

    if (existsCoffee)
        writeln("Drink coffee");

    else
        writeln("Drink tea");

Most experienced programmers use curly brackets even for single statements. (One of the exercises of this chapter is about omitting them.) Having said that, I will now show the only case where omitting the curly brackets is actually better.

The "if, else if, else" chain

One of the powers of statements and expressions is the ability to use them in more complex ways. In addition to expressions, scopes can contain other statements. For example, an else scope can contain an if statement. Connecting statements and expressions in different ways allows us to make programs behave intelligently according to their purposes.

The following is a more complex example written under the agreement that riding to a good coffee shop is preferred over walking to a bad one:

    if (existsCoffee) {
        writeln("Drink coffee at home");

    } else {

        if (existsBicycle) {
            writeln("Ride to the good place");

        } else {
            writeln("Walk to the bad place");
        }
    }

The code above represents the sentences "If there is coffee, drink at home. Else, if there is a bicycle, ride to the good place. Otherwise, walk to the bad place."

Let's complicate this decision process further: instead of having to walk to the bad place, let's first try the neighbor:

    if (existsCoffee) {
        writeln("Drink coffee at home");

    } else {

        if (existsBicycle) {
            writeln("Ride to the good place");

        } else {

            if (neighborIsHome) {
                writeln("Have coffee at neighbor's");

            } else {
                writeln("Walk to the bad place");
            }
        }
    }

Such decisions like "if this case, else if that other case, else if that even other case, etc." are common in programs. Unfortunately, when the guideline of always using curly brackets is followed obstinately, the code ends up having too much horizontal and vertical space: ignoring the empty lines, the 3 if statements and the 4 writeln expressions above occupy a total of 13 lines.

In order to write such constructs in a more compact way, when an else scope contains only one if statement, then the curly brackets of that else scope are omitted as an exception to this guideline.

I am leaving the following code untidy as an intermediate step before showing the better form of it. No code should be written in such an untidy way.

The following is what the code looks like after removing the curly brackets of the two else scopes that contain just a single if statement:

    if (existsCoffee) {
        writeln("Drink coffee at home");

    } else

        if (existsBicycle) {
            writeln("Ride to the good place");

        } else

            if (neighborIsHome) {
                writeln("Have coffee at neighbor's");

            } else {
                writeln("Walk to the bad place");
            }

If we now move those if statements up to the same lines as their enclosing else clauses and tidy up the code, we end up with the following more readable format:

    if (existsCoffee) {
        writeln("Drink coffee at home");

    } else if (existsBicycle) {
        writeln("Ride to the good place");

    } else if (neighborIsHome) {
        writeln("Have coffee at neighbor's");

    } else {
        writeln("Walk to the bad place");
    }

Removing the curly brackets allows the code to be more compact and lines up all of the expressions for easier readability. The logical expressions, the order that they are evaluated, and the operations that are executed when they are true are now easier to see at a glance.

This common programming construct is called the "if, else if, else" chain.

Exercises
  1. Since the logical expression below is true, we would expect this program to drink lemonade and wash the cup:
    import std.stdio;
    
    void main() {
        bool existsLemonade = true;
    
        if (existsLemonade) {
            writeln("Drinking lemonade");
            writeln("Washing the cup");
    
        } else
            writeln("Eating pie");
            writeln("Washing the plate");
    }
    
    But when you run that program you will see that it washes the plate as well:
    Drinking lemonade
    Washing the cup
    Washing the plate
    
    Why? Correct the program to wash the plate only when the logical expression is false.
  2. Write a program that plays a game with the user (obviously with trust). The user throws a die and enters its value. Either the user or the program wins according to the value of the die:
    Value of the die         Output of the program
            1                      You won
            2                      You won
            3                      You won
            4                      I won
            5                      I won
            6                      I won
     Any other value               ERROR: Invalid value
    
    Bonus: Have the program also mention the value when the value is invalid. For example:
    ERROR: 7 is invalid
    
  3. Let's change the game by having the user enter a value from 1 to 1000. Now the user wins when the value is in the range 1-500 and the computer wins when the value is in the range 501-1000. Can the previous program be easily modified to work in this way?