9. Control flow
Like most other programming languages, Mojo provides control structures such as conditions and loops.
9.1. if-else
The most basic control flow is if statement. It takes an expression that must evaluate to a boolean result. The body of the if will be executed if the boolean result is True. It is not allowed to have else without a corresponding if.
if x == 0:
print("X is zero")
In this example, we have an if statement with a condition. The == operator checks for equality, here it checks if the variable x has the value 0.
If you want to execute another body of code when the if expression results in False, you can use else.
if x == 0:
print("X is zero")
else:
print("X is non-zero")
9.2. elif
What if you need to check multiple conditions instead of just one? Mojo provides if-elif-else construct for those cases.
if x == 0:
print("X is zero")
elif x < 0:
print("X is negative")
else:
print("X is positive")
The first if works same as before. The elif is a short form of "else if". It works similar to if and expects the expression to evaluate to True to execute its own body. One thing to note is that if and elif are mutually exclusive and the first one in the sequence of the statements to have expression value as True will execute its body. Therefore care must be taken to correctly order the conditions so that you don’t miss out some edge cases. It is not allowed to have elif without a corresponding if.
if x == 0:
print("X is zero")
elif x == 0:
print("X is zero - but this won't show up")
elif x < 0:
print("X is negative")
else:
print("X is positive")
In the previous code listing, since the if condition already evaluated to True, the second elif will not get executed even if the expression is True.
9.3. Nesting of if
You can nest if inside another if or else or elif. This allows for implementation of a more complex control flow logic.
if x < 0:
print("X is negative")
if x < -5:
print("X is too low")
else:
if x == 0:
print("X is zero")
print("X is positive")
Too many nested conditions could impact readability of the code. In this case consider whether extracting them into separate functions improve readability.
9.4. if as expression
The standard if statement spans over a minimum of 4 lines, sometimes when we just want to assign a value conditionally to a variable, it is a bit verbose. Mojo provides a shorter one line version that could be used in such cases.
var message = "X is positive" if x >=0 else "X is negative"
print(message)
One thing to note here is that the order of the if expression is different from the usual order of if statements.
The anatomy of if-elif-else is shown in the following diagram.
9.5. case
9.6. while
The while loops allow a body of code to be executed repeatedly as long as the condition of the while evaluates to True. The moment the condition evaluates to False, it stops executing the code body within it.
x = 0
while x <= 5:
print("X is ", x)
x = x + 1
The previous code listing will print values 0 to 5.
It is possible to add else clause after the while body. The else body will be executed exactly once when the condition of while evaluates to False.
x = 0
while x <= 5:
print("X is ", x)
x = x + 1
else:
print("X is now greater than 5")
The previous code listing will print "X is now greater than 5" after printing values 0 to 5.
The anatomy of a while loop is shown in the following diagram.
9.7. for
Similar to while, for also provides facility to repeat a code block many times. The main difference is that while is based on an expression evaluating to True whereas for is based on something called an iterator.
In the most simple term, an iterator is something that returns an element when its next method is called. The expression that comes after in within the for loop statement must resolve to an iterable. An iterable is anything that returns an iterator when its iter method is called. In effect, when the for loop is executed, it calls the iterable’s iter method which returns the iterator the for loop works with. For each repetition of the loop, the iterator’s next is called and its result assigned to the variable coming before the in keyword. The iterator must keep track of the state so that the for loop advances to the next element when next is called.
for i in range(0, 5):
print(i)
It is possible to add else clause after the for body. The else body will be executed exactly once when the iteration is finished.
for i in range(0, 5):
print(i)
else:
print("i is now greater than 4")
The anatomy of a for loop is shown in the following diagram.
9.8. Skipping and exiting early from loops
9.8.1. break
If you want to exit the while or for loop early (usually on some condition), then you can use break. This allows early exit from the loop.
x = 0
while x <= 5:
if x > 3:
break
print("X is ", x)
x = x + 1
for i in range(0, 6):
if i > 3:
break
print("i is ", i)
The previous code listing will print values 0 to 3, and will exit the loop as soon as x is greater than 3.
9.8.2. continue
What if you wanted to skip an iteration of the loop? For this case Mojo provides you with continue. The continue keyword would skip all the statements coming after it in the while or for body for exactly one iteration of the loop.
x = 0
while x <= 5:
x = x + 1
if x < 3:
continue # Skip following statements of the while block
print("X is ", x)
for i in range(0, 6):
if i < 3:
continue # Skip following statements of the while block
print("i is ", i)
The previous code listing will skip the print statement coming after it until value of x is greater than or equal to 3.
9.9. Comprehensions
Comprehensions provide a concise way to create lists, sets, or dictionaries. It is usually used to make new lists or sets where each element is the result of some operations applied to each element of a given sequence or iterable, or to create a filtered subsequence of those elements based on a given condition.
9.9.1. List comprehensions
List comprehensions are used to create new lists by applying an expression to each element in an iterable.
var ints = [1, 2, 3, 4, 5]
var squares = [x * x for x in ints]
for s in squares:
print(s)
In the previous code listing, we created a list of squares of integers from another list of integers.
9.9.2. Set comprehensions
Set comprehensions are used to create new sets by applying an expression to each element in an iterable.
var ints2 = [1, 2, 3, 4, 5]
var cubes = {x * x * x for x in ints2}
for c in cubes:
print(c)
In the previous code listing, we created a set of cubes of integers from another list of integers.
9.9.3. Dictionary comprehensions
Dictionary comprehensions are used to create new dictionaries by applying an expression to each element in an iterable.
var ints3 = [1, 2, 3, 4, 5]
var square_dict = {x: x * x for x in ints3}
for e in square_dict.items():
print(e.key, "->", e.value)
In the previous code listing, we created a dictionary where each key is an integer from the list and its corresponding value is the square of that integer.
9.9.4. Filtered comprehensions
As mentioned earlier, comprehensions can also be used to create a filtered subsequence of elements based on a given condition. This can be done using an if clause at the end of the comprehension.
var ints4 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var odds = [x for x in ints4 if x % 2 != 0]
for o in odds:
print(o)
In the previous code listing, we created a list of odd integers from another list of integers by filtering out the even integers.
9.9.5. Nested comprehensions
Comprehensions can also be nested to create more complex data structures.
var matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
var flat = [cell * 2 for row in matrix for cell in row]
for f in flat:
print(f)
In the previous code listing, we created a flat list of doubled values from a matrix of numbers. We should be careful while using nested comprehensions as they can quickly become hard to read and understand. It is often better to use regular loops for complex operations and avoid nesting of comprehensions.