R offers different control flow structures, which allow us to control the flow of execution of a script:


if-else

if (condition) {
    # do something
} else {
    # do something else
}

A simple example cloud look like this:

my.age = 14
if (my.age <18) {
    print('You are a teenager')
} else {
    print('You are an adult')
}
## [1] "You are a teenager"
my.age = 21
if (my.age <18) {
    print('You are a teenager')
} else {
    print('You are an adult')
}
## [1] "You are an adult"

For such simple if-else condition there exists a vectorized version, and hence a more efficient version.

my.age <- 15
ifelse(my.age <= 18, 'You are a teenager', 'You are an adult')
## [1] "You are a teenager"
my.age <- 45
ifelse(my.age <= 18, 'You are a teenager', 'You are an adult')
## [1] "You are an adult"

Another related control flow element is the switch() command, which selects one of a list of alternatives. Let us create a function called centre, which computes either the mean or the median of a given vector.

centre <- function(x, type) {
  switch(type,
         mean = mean(x),
         median = median(x))
}

Let us construct a vectorv.

v <- c(1,2,3,4,5,1000)

Now we call the centre() function, first computing the mean and thereafter the median of the vector v.

centre(v, 'mean')
## [1] 169.1667
centre(v, 'median')
## [1] 3.5

Loops

Loops are very powerful techniques if we want to repeat some task. A for loop works on an iterable variable of finite length. For loops are applied if we know in advance how often a task shall be repeated.

Let us use a for loop to print each number between one and 10.

for (i in 1:10) {
    print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

We may as well loop through any iterable object, such as a vector of strings. Let us create a vector of strings.

beatles <- c("John", "George", "Paul", "Ringo")

We may either access the items directly…

for (i in beatles) {
  print(i)
}
## [1] "John"
## [1] "George"
## [1] "Paul"
## [1] "Ringo"

or we access the item via indexing

for (i in 1:length(beatles)) {
  print(i)
  print(beatles[i])
}
## [1] 1
## [1] "John"
## [1] 2
## [1] "George"
## [1] 3
## [1] "Paul"
## [1] 4
## [1] "Ringo"

Sometimes it makes sense to construct a nested loop to achieve a particular task.

M <- matrix(1:10, 5)
M
##      [,1] [,2]
## [1,]    1    6
## [2,]    2    7
## [3,]    3    8
## [4,]    4    9
## [5,]    5   10

We now loop through all rows and columns of the matrix M and print out the value at the index \(M[i,j]\).

for (i in seq(nrow(M))) {
  for (j in seq(ncol(M))) {
    val <- M[i, j]
    print(paste("row index:", i, "column index:", j, "value:", val))
    }
}
## [1] "row index: 1 column index: 1 value: 1"
## [1] "row index: 1 column index: 2 value: 6"
## [1] "row index: 2 column index: 1 value: 2"
## [1] "row index: 2 column index: 2 value: 7"
## [1] "row index: 3 column index: 1 value: 3"
## [1] "row index: 3 column index: 2 value: 8"
## [1] "row index: 4 column index: 1 value: 4"
## [1] "row index: 4 column index: 2 value: 9"
## [1] "row index: 5 column index: 1 value: 5"
## [1] "row index: 5 column index: 2 value: 10"

A while loop repeats a task until a condition is met. This means that we do not need to know in advance how many times the loop will be executed. Be sure there is a way to exit out of a while loop, otherwise you program will run forever.

i <- 1 # set i to 1
while (i < 10) { # continue as long as i < 10
    print(i) # print i
    i <- i + 1 # increase i by one for each step
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9

Other useful commands to control the execution of your code is the break and the next statement.

We redo the while loop from above, this time however, we build in an error, which would cause the loop to run infinitely. However, by introducing the break statement we make sure the the loop stops no matter what after 50 iterations.

i <- 1 # set i to 1
while (i < 10) { # continue as long as i < 10
    i <- i - 1 # ERROR! Here we decrease i by one for each step, hence the condition i < 10 is always met!
    if (abs(i) == 50) { # if i is 50 or -50 we break out of the loop
      print("Iteration stopped!")
      break
    }
}
## [1] "Iteration stopped!"
# sanity check
print(i)
## [1] -50

The next statement is used to skip the current loop execution and restarts the next iteration.

In order to print only even numbers and skip odd numbers of a given sequence we may write the following code (if you never encountered the modulo operator %% before, google it!):

s <- seq(1,25,1)
for (i in s) {
    if (i%%2 == 1) { # modulo operator
        next
    } else {
        print(i)
    }
}
## [1] 2
## [1] 4
## [1] 6
## [1] 8
## [1] 10
## [1] 12
## [1] 14
## [1] 16
## [1] 18
## [1] 20
## [1] 22
## [1] 24

Guess a number

To conclude this section we apply our knowledge of functions and control statements to build a small program, called guess_a_number. The program is all about guessing a random integer within a given range of guesses.

guess_a_number<- function(max_value = 100, number_of_trials = 10)
{
  ## the computer guesses a random number 
  ## between 0 and max_value (default = 100)
  vector_of_integers <- seq(1,max_value,1) 
  num <- sample(vector_of_integers,1)
  
  ## set trial value to 1
  trial <- 1
  
  ### utility function to account for user input
  ## ask user for a guess
  ask.for.guess <- function(){
    cat("Guess a number between 0 and", max_value, "\n")
    guess <- readline(prompt = "Enter an integer: ")
    # make sure user entered an integer
    if(!grepl("^[0-9]+$", guess))
      {
      cat("Please provide an integer\n")
      return(ask.for.guess())
      }
    return(as.integer(guess))
  }
    
  ## start the game
  ## set conditions for game over
  cat(paste("You have", number_of_trials, "trials to guess the number!\n"))
  while(guess != num & trial <= number_of_trials)
    {
    guess <- ask.for.guess()
    if (guess == num)
      {
      cat("Congratulations,", num, "is right.\n")
      break # break as game is over
      }
    else if (guess < num)
      {
      cat("The number is bigger!\n")
      }
    else if(guess > num)
      {
      cat("The number is smaller!\n")
      }
    cat(paste(number_of_trials-trial, "trials left!\n\n"))
    trial <- trial+1 # increase the number of trials
    }
  if (guess != num){
      cat(paste("GAME OVER, the number was", num))
  }
}

Now copy the code cell above to your R console and give it a try!

guess_a_number()

Feel free to adapt the function and play around with the arguments max_value and number_of_trials.