Basic flow statements
What are basic flow statements? These are several statements which help you to structure the program code in a way that allows you to do different action(s) based on the data stored in particular variables. We will learn how to execute just part of the code if a certain condition is met (the condition could be a pretty complex Boolean expression). Then, we will find a way to execute different actions several times in a loop. The next thing will be to learn how to repeat things until a condition is met and to stop executing statements once the condition is not satisfied. Using flow-control statements, we can construct pretty complex code chunks, similar to what we can express with regular text writing. To develop a program, we should first create an algorithm (a sequence of steps) which leads to the desired result, taking into account all external and internal conditions. Based on this sequence, we can then develop a program, using all flow operators. But let's get familiar with some forms of them.
The if statements – how to control the code flow
This is how we can branch our code logic based on some data stored in a variable:
let num = 5
if num % 2 == 0 {
print("The number \(num) is even.")
} else {
print("The number \(num) is odd.")
}
The general pattern of an if
statement is organized as follows:
var logicalCheck = 7 > 5
if (logicalCheck) {
//code which will be executed if the logical check is evaluated to true
} else {
//code which will be executed if the logical check is evaluated to false
}
We know that the if
clause gives us huge freedom to shape the code that will be executed (evaluated). An application may handle many different cases, but only the code that fulfills the conditions encoded in our solution will be triggered.
Loops
Let's learn how to implement repetitive tasks. There are several ways to do that, using different loops: while
, for...in
, and repeat...while
. The most popular one is the for...in
loop. Here is what the basic form looks like:
let collection = [1, 2, 3]
for variable in collection {
//do some action
}
The code will be interpreted like this: the variable will be set to all possible values, which are stored in the collection. If the collection is empty, then no code will be executed. If there are some elements, then the body of the loop (the code in curly braces) will be executed for each element of the collection. The variable loops through every single element and can be used in code.
We need an example to illustrate this. Let's use the following code to print all numbers from 1
to 10
, inclusive:
var sum = 0
for index in 1...10 {
sum += index
print("(index)")
}
print("Sum: \(sum)")
//sum is equal to 55
The sum of all numbers from 1
to 10
is stored in a separate variable and the code prints every single number on a new line. The sequence defined with 1...10
is converted to a collection (we can think of it as an array), which is fueling the for...in
loop.
Note
We can use variables or constants to define custom ranges of numbers.
Take a look at the following code:
let threeTimes = 3
for _ in 1...threeTimes {
print("Print this message.")
}
Using _
(underscore) we declare that the argument should ignore the values set in the variable, and it doesn't matter to the rest of the code. The code will print three times: Print this message
.
The while loops
The while
loops execute the body of the loop (list of the statements in the body part) until the condition is evaluated to false
.
There are two types of while
loops. There is the classical while
loop, which checks the condition, and, if it holds, then the code in the body is executed. Then the check is performed again and everything is repeated until the condition is evaluated to false
. The other variant is the repeat...while
loop, which first executes the body, and then does the check. The second type is executed at least once, compared to the first one, which could be completely skipped:
var i = 1 let max = 10 var sum = 0 while i <= max { sum += i i += 1 } print("Sum: \(sum)")
The code sums all numbers from 1
to 10
. The condition will be broken once i
reaches 11
.
We can use repeat...while
to do the same:
var i = 1 let max = 10 var sum = 0 repeat { sum += i i += 1 } while i <= max print("Sum: \(sum)")
We can use while
to implement the repeat...while
loops and the reverse, but with slight modifications. The best rule for picking the right type of loop is to know whether the sequence should be executed at least once. Executing once means that it's much easier to implement it using repeat...while
; otherwise, the classical while
loop is the best choice.
There are some special conditions which we should handle, but to do so, let's see what they are.
We can use the special words—continue
and break
—to trigger special behavior while we are in a loop. The continue
statement is used when you want to stop the current iteration of the loop and start over. When using this, be careful that you change the value in the condition; otherwise, the loop could be an infinite one, which means that your program won't end.
The break
statement is used once we want to stop the entire loop. Be careful when you have nested loops. The break
statement stops the current iteration immediately, and then jumps to the very first line after the end of the innermost loop, which contains the break
statement. If you want to break two or more nested loops, then you have to find an appropriate way to do so. To be explicit when breaking nested loops, you may use labeled statements. It is a convenient way to give a name of a loop and then to change the flow when using break
. It's good to know that break
may be used as part of a switch
statement. This will be discussed in the next part.
There are a few other special words, such as return
, throw
, and fallthrough
which change the default order of execution of the code. We will get familiar with these later.
The switch statement
A switch
statement is a concise way to describe a situation where we have several possible options to pick from and we don't want to write a lot of boilerplate code using the already familiar if
statement.
Here is the general pattern of a switch
statement (please note that this is not a valid Swift code):
switch a-variable-to-be-matched { case value-1: //code which will be executed, if variable has value-1 //we need at least one valid executable statement here (comments are not an executable statement) case value-2, value-3: //code which will be executed, if variable has value-2 or value-3 default: //code which will be executed, if variable has value different from all listed cases }
What we see is that switch
has many possible cases, each one starting with the special word case
, and then a specific value. Swift supports specific value matching, but it supports more complex rules for pattern matching. Each case could be considered as a separate if
statement. If one case is activated, then all others are skipped. The default
case is a specific one and is triggered if there is no match with any other case. The default
case appears at the end, and it's defined with the special word default
.
We can use break
to interrupt execution of the code in a case
statement. If we want to have an empty case
statement, it's good to add break
.
We have some specifics with the implementation of switch
in Swift, which are new when compared to the other programming languages, but they improve the readability of the code. First, there is now a way to have an empty body of a specific case. To be correct, we have to add at least one valid statement after the case. There is no implicit fallthrough after each case. This means that once the last executable statement in a case
branch is triggered, we are continuing after the switch
statement. Nothing else that is part of the switch
statement will be executed. We could consider that every case
statement has a hidden break at its very end. Next, we need the special word fallthrough
to simulate the regular behavior of the switch. Another interesting thing is that we can have interval matching, tuples matching, and value bindings. Finally, we can use the where
clause if we want to express some dependency between the data which should be matched. It's also possible to list several cases if they have to share the code which should be executed. They have to be separated with ,
.
Here is code that shows how easy and smart switch
is:
let point = (1, 1) switch point { case let (x, y) where x == y: print("X is \(x). Y is \(y). They have the same value."); case (1, let y): print("X is 1. Y is \(y). They could be different."); case (let x, 1): print("X is \(x). Y is 1. They could be different."); case let (x, y) where x > y: print("X is \(x). Y is \(y). X is greater than Y."); default: print("Are you sure?") }