Flow control
Overview
Teaching: 25 min
Exercises: 5 minQuestions
How do we get our code to react dynamically?
How do we delegate repetitive task and decisions?
How do we make our software robust to a wider set of use cases?
Objectives
Understand the role of flow control in managing the order of statement execution.
Understand the role and combination of logical and comparison operators
Often, we want to perform different operations in our code based upon dynamic conditions. To explore this idea, we are going to pretend we have two sensors. The first representing a temperature, and the second representing if it there is rainfall. Our temperature is a numeric value, and our rainfall is a logical (Boolean). To declare those variables, type:
temp_reading <- 16
rainfall <- TRUE
Then place the following code into your script pane:
if (rainfall == TRUE){
print("advise user to take umbrella")
}
![/Introduction_to_R/Flow%20diagrame%20for%20if%20condition](/Introduction_to_R/fig/ifflow1.png)
Evaluating the order of operations
Run the script using the ‘run’ button on the top right of the pane (pay attention to where your cursor is in the script pane when you run the script).
"advise user to take umbrella"
From observing the output in the console and from a brief inspection of the code, it should be evident that we are evaluating the variable rainfall. Specifically, we are checking for equivalence (==). If the outcome is of the check is valid then we perform any code within the brackets.
Now modify your code to look like this:
if (rainfall){
print("advise user to take umbrella")
}else{
print("leave your umbrella at home")
}
Note: with logical variables, we don’t have to check for equivalence (if (logical_variable) is if (TRUE/FALSE)).
![/Introduction_to_R/Flow%20diagrame%20for%20if%20condition](/Introduction_to_R/fig/ifflow2.png)
Change the order
Now change the rainfall variable to ‘FALSE’ and run the script again.
rainfall <- FALSE
"leave your umbrella at home"
Our code now reacts differently to different input values. You can combine ‘if’, ‘else if’, and ‘else’ statements to control the flow of your code.
Conditional statements
-
‘if’ – used to execute a block of code if a condition is true.
-
‘else if’ – extends an ‘if’ statement to only check this condition if the previous ‘if’ or ‘else if’ condition was resolved as false.
-
‘else’ – extends an ‘if’ statement, will execute if none of the preceding ‘if’ conditions are true.
Comparison operators
We have encountered ‘==’, it is used to check for equivalence. There are other comparison operators available to us.
-
‘<’ Less than
-
‘<=’ Less than or equal to
-
‘>’ Greater than
-
‘>=’ Greater than or equal to
-
‘==’ equivalent
-
‘!=’ not equivalent
Intuitively you might think that some of these operators are going to work only for numeric values (i.e., how can “apple” < FALSE). We could try a few tests to get a feel for how it works:
TRUE == FALSE
FALSE
As we would expect.
TRUE == TRUE
TRUE
As we would expect.
"four" < "five"
FALSE
R is clearly not comparing on the meaning of characters.
"five" < "six"
TRUE
We are comparing alphabetically.
"apple" < FALSE
TRUE
What is happening here? Why is “apple” less than FALSE?
R is attempting to interpret your request; therefore, it is coercing data types. Coercion follows a hierarchy where items can be converted from lower to higher but not higher to lower.
- Lowest
- Logical: Logical values (TRUE or FALSE) are lowest in hierarchy.
- Integer values. (True becomes 1, False becomes 0)
- Floating-point values. (5 becomes 5.0)
- Character. The highest in hierarchy (TRUE becomes ‘TRUE’, 5.0 becomes “5.0”)
- Highest
We are comparing “apple” < “FALSE” alphabetically. Try:
"zoo" < FALSE
FALSE
Now the word is alphabetically greater than “FALSE” we should see FALSE as the response.
Question
Why do we care about “apple” < FALSE ?
We are never going to want to compare “apple” to FALSE, that is not the issue. The issue at hand is that if you have made a mistake and you are comparing the wrong variables you may not get an error. You will just be getting unexpected behaviour from your software which can be very difficult to catch. We will look more at coercion and data types when we deal with manipulating data sets.
Declare a new variable:
storm <- TRUE
Replace the code in your script with this:
if (storm == TRUE){
print("stay at home")
}else if (temp_reading < 10){
print("take a coat")
if (rainfall){
print("and take an umbrella")
}else{
print("and leave your umbrella at home")
}
}else{
print("leave your coat")
if (rainfall){
print("and take an umbrella")
}else{
print("and leave your umbrella at home")
}
}
![/Introduction_to_R/Flow%20diagrame%20for%20if%20condition](/Introduction_to_R/fig/ifflow3.png)
Keeping things clear
We now have our old conditional statements inside a new conditional statement. This is referred to as ‘nested’. If your code becomes overly nested it can impact readability and maintainability. It is good practice to keep your workflow as simple as possible, this can be made easier by spending time on design and regular refactoring.
Note: Refactoring is the process of restructuring code, not to change the functionality but to improve factors like readability, maintainability, efficiency.
Now you have three variables storm, temp_reading and rainfall. You can modify the values and see how this changes the order statements are executed in a program.
Question
What do you think might be problematic about this example?
There a several concerns that you may have. The ones I want to draw your attention to are
- No defensive programming – What happens if we ‘accidently’ switch temp_reading and storm variables?
- Hardcoded variables – Should a user preference be hard coded, what implications might this have?
- Redundancy - When we have repetition in code it can lead to problems with updating (e.g., if we want to make a change we must edit code in multiple places, this could easily introduce errors).
We will look at these issues in more detail and consider ways to fix them as we go through more examples.
Logical operators
Logical operators can be used to combine multiple comparison operators (e.g., if x < y and if x > z).
The most common are
-
AND ‘&&’
-
OR ‘||’
-
NOT ‘!’ (negation)
Let’s try an example, add a new variable and add this new code to your script:
day = "sunday"
if (storm == FALSE && (day == "monday" || day == "tuesday"|| day == "wednesday" || day == "thursday" || day == "friday")){ #only a storm will stop me from working mon-fri
print("go to work")
}else{
print("stay at home")
}
This may look more complicated, but we are combining the conditionals we have already considered but now with logical operators (i.e., IF no storm AND the day is (Monday to Friday)).
Question
What do you think the consequence of removing the brackets will be?
Hint: Change storm <- TRUE, day <- “wednesday”.
What happens?
What should happen?
You need to ensure that you are explicit with the conditional operations by using brackets, this will allow you to combine without encountering unwanted behaviour.
One direction we can take this is to reduce the number of logical operators. We could change the code to:
working_days <- c("monday","tuesday","wednesday","thursday", "friday")
Then we could use ‘%in%’ (worth checking ‘%in%’ through the help menu):
if (storm == FALSE && day %in% working_days){ #only a storm will stop me from working mon-fri
print("go to work")
}else{
print("stay at home")
}
We have covered a brief introduction to the basic building blocks of flow control. Hopefully you have grasped, with the few tools we have considered, that you can develop sophisticated flow control. There are more tools to consider to be fully competent on all aspects of flow control. However, they fall outside of the scope of this course.
We have included a source to provide a starting point for you to explore advanced flow control if you wish to (e.g., switch statements, loops etc.):
Starting point for advanced flow control
Note: If you have coding experience you may have used loops before. In R loops can be problematic due to performance issue with memory allocation on large datasets. It is not a beginner-friendly topic; this in part, is why we have excluded them from this introduction to workflow.
Key Points
Flow control is an important technique you need to learn to create useful software
Whenever possible simplicity is the best option
Plan & refactor