Chapter 6 Control flow in R

In this chapter, we will cover basically the so-called Control flow in R with the following topics -

  • Conditional execution
    • If-else statements
  • Loops
    • while loop
    • for loop
  • User defined funstions in R
    • Functions without return
    • Functions with return

6.1 Conditional execution

6.1.1 If-else statement

The very basic conditional execution is the if and if-else statements. Let’s think you have a condition of do something (i.e. choose from a set of data-points or perform a mathematical operation) using R, you use if(condition){do something}. For example -

x <- 10
if(x == 10){
  print("x is equal to 10")
}
## [1] "x is equal to 10"

If you want to add more twist to the condition, you bring in else statement - if(condition){do something} else{do the alternative}. Like here -

x <- 11
if(x == 10){
  print("x is equal to 10")
} else{
  print("x is not equal to 10")
}
## [1] "x is not equal to 10"

You are not quiet content with the arguments here, you want to add more twists to the conditions, you add an intermediate else if condition -

x <- 11
if(x > 20){
  print("x is greater than 20")
} else if(x <= 20 & x >= 10){
  print("x is between 10 and 20")
} else{
  print("x is less than  10")
}
## [1] "x is between 10 and 20"

6.1.2 Short exercise

You have a job to grade 20 math scripts (the score ranges from 0 to 10) from a class of yr 7 students according to the following scale -

grade A: 8-10
grade B: 6-7
grade C: 4-5
grade F: 0-3

If a student scored 5, what’s the allocated grade? Write a set of conditional if else statements to answer the question.

6.2 Loops

Loops are efficient ways to perform some mathematical operation iteratively, like - keep walking until you reach your destination (using while loop) or read each element of a vector, add one to it and do the same to the next (using for loop).

6.2.1 while loop

The basic structure of while loop is while(condition){execute some action}, like -

# Think logically before executing the following code snippet -
# a <- 10
# while(a < 20){
#  print("a is less than 20")
# }

There are two ways to tackle this -

  1. you can increase the value of a (in each iteration) -
a <- 10
while(a < 20){
  print("a is less than 20")
  a <- a + 1
  print(paste0("Because value of a is now ", a))
}
## [1] "a is less than 20"
## [1] "Because value of a is now 11"
## [1] "a is less than 20"
## [1] "Because value of a is now 12"
## [1] "a is less than 20"
## [1] "Because value of a is now 13"
## [1] "a is less than 20"
## [1] "Because value of a is now 14"
## [1] "a is less than 20"
## [1] "Because value of a is now 15"
## [1] "a is less than 20"
## [1] "Because value of a is now 16"
## [1] "a is less than 20"
## [1] "Because value of a is now 17"
## [1] "a is less than 20"
## [1] "Because value of a is now 18"
## [1] "a is less than 20"
## [1] "Because value of a is now 19"
## [1] "a is less than 20"
## [1] "Because value of a is now 20"

But, see, there’s a logical fault in the coding.

  1. Using break to break out of the loop
a <- 10
while(a < 20){
  a <- a + 1
  if(a < 20){
    print("a is less than 20")
    print(paste0("Because value of a is now ", a))
  }else{
    break
  }
  
}
## [1] "a is less than 20"
## [1] "Because value of a is now 11"
## [1] "a is less than 20"
## [1] "Because value of a is now 12"
## [1] "a is less than 20"
## [1] "Because value of a is now 13"
## [1] "a is less than 20"
## [1] "Because value of a is now 14"
## [1] "a is less than 20"
## [1] "Because value of a is now 15"
## [1] "a is less than 20"
## [1] "Because value of a is now 16"
## [1] "a is less than 20"
## [1] "Because value of a is now 17"
## [1] "a is less than 20"
## [1] "Because value of a is now 18"
## [1] "a is less than 20"
## [1] "Because value of a is now 19"

Look carefully, though break is applied in the if-else statement, it affects on the loop that it is part of.

6.2.1.1 Short exercise on while loop

You are giant and in every step you cover 15 meters. Your home is 100 km away from your current position. How many steps do you need to walk before you reach your home?

6.2.2 for loop

Say, you have a vector b <- c("a","b","c","d","e") and you want to read the elements iteratively and then print them, what you can do is -

b <- c("a","b","c","d","e")

# looping through the contents of a vector directly
for(i in b){
  print(i)
}
## [1] "a"
## [1] "b"
## [1] "c"
## [1] "d"
## [1] "e"
# looping through the contents of a vector using indexing
for(i in 1:length(b)){
  print(b[i])
}
## [1] "a"
## [1] "b"
## [1] "c"
## [1] "d"
## [1] "e"

Looping through sequential numbers (in some programming language you have to define the initial value and the increment in this line) -

for(i in 1:5){
  print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5

For a bit better understanding -

for(i in 1:5){
  print(paste0("The current value of i is ", i))
}
## [1] "The current value of i is 1"
## [1] "The current value of i is 2"
## [1] "The current value of i is 3"
## [1] "The current value of i is 4"
## [1] "The current value of i is 5"

Look carefully at this piece of code to understand the nested loop and the change of the values of i and j

for(i in 1:5){
  for(j in 6:10){
    print(paste("Now i is", i, "and j is", j))
  }
}
## [1] "Now i is 1 and j is 6"
## [1] "Now i is 1 and j is 7"
## [1] "Now i is 1 and j is 8"
## [1] "Now i is 1 and j is 9"
## [1] "Now i is 1 and j is 10"
## [1] "Now i is 2 and j is 6"
## [1] "Now i is 2 and j is 7"
## [1] "Now i is 2 and j is 8"
## [1] "Now i is 2 and j is 9"
## [1] "Now i is 2 and j is 10"
## [1] "Now i is 3 and j is 6"
## [1] "Now i is 3 and j is 7"
## [1] "Now i is 3 and j is 8"
## [1] "Now i is 3 and j is 9"
## [1] "Now i is 3 and j is 10"
## [1] "Now i is 4 and j is 6"
## [1] "Now i is 4 and j is 7"
## [1] "Now i is 4 and j is 8"
## [1] "Now i is 4 and j is 9"
## [1] "Now i is 4 and j is 10"
## [1] "Now i is 5 and j is 6"
## [1] "Now i is 5 and j is 7"
## [1] "Now i is 5 and j is 8"
## [1] "Now i is 5 and j is 9"
## [1] "Now i is 5 and j is 10"

Or, the following code snippet -

my_matrix <- matrix(1:18, nrow = 3, byrow = T)

for(i in my_matrix){
  print(i)
}
## [1] 1
## [1] 7
## [1] 13
## [1] 2
## [1] 8
## [1] 14
## [1] 3
## [1] 9
## [1] 15
## [1] 4
## [1] 10
## [1] 16
## [1] 5
## [1] 11
## [1] 17
## [1] 6
## [1] 12
## [1] 18

6.2.3 Short exercise on For loop

But you do not want to print the content of my_matrix by column, rather you want to do it by row. How would you do it using nested for loops?

6.3 User defined functions

By now, we have already seen some functions, like - print() and mean() that are built-in to your R environment. Sometimes they are not enough, you may need to perform some extra mathematical operations. Here comes the cool feature of R programming language, you can define your own functions. The basic structure of a fucntion is -

function_name <- function(input1, ..., inputN=default_value){
  code to execute
  return() # if any
}

6.3.1 Function without return()

Let’s first look at a very simple function. This does not take any input as prerequisit and will not return any value -

printOnly <- function(){
  print("hi")
}
printOnly() # remember to put a parentheses afterwards to call the function.
## [1] "hi"

Now, define the same function with an input as argument (with a default value as well) -

printOnly <- function(name="David"){
  print(paste("hi", name))
}
printOnly()
## [1] "hi David"
# check out what happens if there is no default value assigned to the input argument and you call the function without any value.

6.3.2 Function with a return()

So far, we have been, basically, printing something with the function. We actually want is to return the result. Now we will look into a function that returns a result and you can use it for later usage if you save it in a variable. Say, we want to add two numbers by a user defined function called add -

add <- function(num1, num2){
  result <- num1 + num2
  return(result)
}

add(2,33)
## [1] 35

Now, we can assign the output of the function to a variable (say, result) -

add <- function(num1, num2){
  result <- num1 + num2
  return(result)
}

result <- add(2,33)

Check that nothing is printed on the console as it returned some value and the value is now saved in the variable result. If you type in result in the console, now you can see the value.

6.3.3 Scoping

There is another interesting feature you should keep in mind while scripting and defining your own function(s) - The scoping. The value of a variable can change inside or outside of your function depending of it’s position of definition and under what scope (either global or local) you call it -

name <- "My name is David" # global scope
loc <- "I live in Edinburgh" # global scope

stay <- function(loc){
  print(name)
  loc <- "I live in London" # redefining locally and work within the scope of the function
  print(loc)
}
stay(loc)
## [1] "My name is David"
## [1] "I live in London"
# But, if you print "loc" now, see what happens......
print(loc)
## [1] "I live in Edinburgh"

6.3.3.1 Short exercise on function

Create a function called div to divide two numbers. If the denominator is 0, then the function prints out “Please provide a non-zero denominator”.

Now check these two expression div(2,0) and div(2,3)