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
-
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 isfalse
. - 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
- 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?