Loops execute a block of code as long as a specified condition is reached.

We discuss two kinds of loops in R: for loops and while loops.


for loops

A for loop is used for iterating over items in a vector (or an array or a list). It is useful if we know in advance the set of values that we want to iterate over.


How it works:

for (variable in vector) expression

variable is the loop variable. For each variable in vector, expression is called once; the value of variable is then updated. vector can be a vector, array, list, etc.

expression is often a grouped expression. In that case, when we have multiple expressions, the expressions must be surrounded by curly braces.

for (variable in vector) {
  expression 1
  expression 2
  ...
}

It’s conventional to use very short variable names like i or j to iterate over a vector.

for (i in seq(from = 5, to = 25, by = 5)) print(i)
## [1] 5
## [1] 10
## [1] 15
## [1] 20
## [1] 25

In loops, we must use the print() function if we want to output a result.

for (i in 1:5) {
  i
}

Example 1:

Append each value in c(0,1,2,3,4,5) to an empty vector.

values <- c(0,1,2,3,4,5)

# define an empty output object
vec <- numeric(0)
# vec <- vector("numeric", 0) 

for (i in seq_along(values)) {
  vec <- c(vec, values[i])
}
print(vec)
## [1] 0 1 2 3 4 5

This is the common “growing our results” scenario. Remember to define the empty output object before filling in the values.


Notes:

  1. vec is our output numeric vector. If we’re generating data, make sure that we set up the output container.

  2. seq_along(values) generates a sequence 1,2,3,4,5,6. Here we use seq_along() to get the index of each element in vector values, so that we can use values[i] to access its elements.

  3. vec <- c(vec, values[i]) flattens vectors and combines them into one single vector. When the arguments to c() are themselves vectors, it flattens them and combines them into one single vector.

  4. for (i in seq_along(values)) iterates over each item in values.

vec i values[i]
empty
0 1 0
0,1 2 1
0,1,2 3 2
0,1,2,3 4 3
0,1,2,3,4 5 4
0,1,2,3,4,5 6 5
  1. In this case, we may substitute i in seq_along(values) with i in 1:length(values) .
vec <- c()
for (i in 1:length(values)) {
  vec[i] <- values[i]
}
print(vec)
## [1] 0 1 2 3 4 5

However, there are times when iterating over 1:length(x) will fail; that’s when x is empty and length(x) is 0.

x <- vector("numeric")
1:length(x)
## [1] 1 0

Therefore, it is recommended that we use seq_along(x) whenever we can. It always returns a value the same length as x.

x <- vector("numeric")
seq_along(x)
## integer(0)

Example 2:

Get the first 10 Fibonacci numbers. The Fibonacci numbers form a sequence, such that each number is the sum of the two preceding ones, starting from 0 and 1.

f <- numeric(10)
f[1] <- 0
f[2] <- 1
for (i in 3:10) {
  f[i] <- f[i - 2] + f[i - 1]
}  
print(f)
##  [1]  0  1  1  2  3  5  8 13 21 34

Notes:

f i f[i] f[i-2] f[i-1]
0 1 0
0,1 2 1
0,1,1 3 1 0 1
0,1,1,2 4 2 1 1
0,1,1,2,3 5 3 1 2
0,1,1,2,3,5 6 5 2 3
0,1,1,2,3,5,8 7 8 3 5
0,1,1,2,3,5,8,13 8 13 5 8
0,1,1,2,3,5,8,13,21 9 21 8 13
0,1,1,2,3,5,8,13,21,34 10 34 13 21

Example 3:

Count how many 2 occurs in the vector rep(1:4, each = 2, times = 3).

vec <- rep(1:3, each = 2, times = 2)
c <- 0
for (i in seq_along(vec)){
  if (vec[i] == 2){
    c <- c + 1
  }
}

print(c)
## [1] 4

Notes:

i vec[i] TRUE c
1 1 0 0
2 1 0 0
3 2 1 1
4 2 1 2
5 3 0 2
6 3 0 2
7 1 0 2
8 1 0 2
9 2 1 3
10 2 1 4
11 3 0 4
12 3 0 4

In real life, you should just use the vectorized sum(). What we did was just to understand how a for loop works.

vec <- rep(1:4, each = 2, times = 3)
sum(vec == 2)
## [1] 6

Note: Wherever vectorized operations are possible, we should avoid for loops. for loops in R run much slower than their vectorized equivalents.


Example 4:

Print the numbers from 1 to 100. Print “Fizz” for multiples of 3, print “Buzz” for multiples of 5, and print “FizzBuzz” for multiples of both 3 and 5. Hint: Use the modulus operator %%.

Solution 1: a naive for loop

vec <- c()
for (n in 1:100){
  if (n %% 3 == 0){ vec[n] <- "Fizz" } 
  else if (n %% 5 == 0){ vec[n] <- "Buzz" } 
  else if (n %% 3 == 0 & n %% 5 == 0){ vec[n] <- "FizzBuzz" } 
  else { vec[n] <- n }
}

print(vec)
##   [1] "1"    "2"    "Fizz" "4"    "Buzz" "Fizz" "7"    "8"    "Fizz" "Buzz"
##  [11] "11"   "Fizz" "13"   "14"   "Fizz" "16"   "17"   "Fizz" "19"   "Buzz"
##  [21] "Fizz" "22"   "23"   "Fizz" "Buzz" "26"   "Fizz" "28"   "29"   "Fizz"
##  [31] "31"   "32"   "Fizz" "34"   "Buzz" "Fizz" "37"   "38"   "Fizz" "Buzz"
##  [41] "41"   "Fizz" "43"   "44"   "Fizz" "46"   "47"   "Fizz" "49"   "Buzz"
##  [51] "Fizz" "52"   "53"   "Fizz" "Buzz" "56"   "Fizz" "58"   "59"   "Fizz"
##  [61] "61"   "62"   "Fizz" "64"   "Buzz" "Fizz" "67"   "68"   "Fizz" "Buzz"
##  [71] "71"   "Fizz" "73"   "74"   "Fizz" "76"   "77"   "Fizz" "79"   "Buzz"
##  [81] "Fizz" "82"   "83"   "Fizz" "Buzz" "86"   "Fizz" "88"   "89"   "Fizz"
##  [91] "91"   "92"   "Fizz" "94"   "Buzz" "Fizz" "97"   "98"   "Fizz" "Buzz"

Solution 2: a better for loop

output <- vector()

for (i in 1:100) {
  output[i] <- ""
  if (i %% 3 == 0) {output[i] <- paste(output[i], "Fizz")}
  if (i %% 5 == 0) {output[i] <- paste(output[i], "Buzz")}
  if (output[i] == "") {output[i] <- i}
}

print(output)

(Reference: Rory Spanton. (2020). How to Solve the FizzBuzz Problem in R. https://towardsdatascience.com/how-to-solve-the-fizzbuzz-problem-in-r-c62e7e6c959a)


Notes:

  1. Solution 1:
  • Create an empty vector.
  • Mark multiples of 3 as “Fizz”.
  • Mark multiples of 5 as “Buzz”.
  • Mark multiples of 3 and 5 as “FizzBuzz”.
  • Mark the remaining elements as the original numbers. The resulting vector is a character vector.
  1. Solution 2:
  • Create an empty vector.
  • Mark all elements as an empty string "".
  • if (i %% 3 == 0) {output[i] <- paste(output[i], "Fizz")} is evaluated first. If an element is a multiple of 3, “Fizz” is pasted to an empty string, resulting in “Fizz”.
  • Next, if (i %% 5 == 0) {output[i] <- paste(output[i], "Buzz")} is evaluated, which works in the same way as the first conditional statement.
  • Lastly, if an element is a multiple of both 3 and 5, “Buzz” will be pasted to “Fizz”. Remaining elements are marked by the original numbers and coerced to characters.

Nested loops

A nested loop is a loop inside a loop. The “inner loop” will be executed one time for each iteration of the “outer loop”.


Example:

Write an R program which takes two digits m (row) and n (column) as input and generates a two-dimensional array. The element value in the i-th row and j-th column of the array should be i*j (i = 1.., m; j = 1.., n).

m <- 3
n <- 4

row <- 1:m
col <- 1:n
a <- matrix(m*n, nrow = m, ncol = n)
for (i in row){
  for (j in col){
    a[i,j] <- i*j
  }
}

print(a)
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    2    4    6    8
## [3,]    3    6    9   12

Notes:

If m=3 and n=4, we get a matrix of 3 rows and 4 columns.

i j i*j
1 1 1
1 2 2
1 3 3
1 4 4
2 1 2
2 2 4
2 3 6
2 4 8
3 1 3
3 2 6
3 3 9
3 4 12

while loops

for loops are useful if we know in advance the set of values to iterate over. If we don’t know, while loops allows for more flexible specifications.

while loops repeat an expression while a condition is true.


How it works:

while (condition) expression

i <- 5
while (i <= 25) {
  print(i)
  i <- i + 5
}
## [1] 5
## [1] 10
## [1] 15
## [1] 20
## [1] 25

Note: We can rewrite any for loop with while instead. This means while is more flexible than for. So we should use for wherever possible. It’s good practice to use the least flexible solution to a problem.


Example:

Write an R program to guess a number between 1 to 9. Hint: User is prompted to enter a guess. If the user guesses wrong then the prompt appears again until the guess is correct. On successful guess, the program will exit.

target_num <- sample(1:9, 1)
guess_num <- 0

while (guess_num != target_num){
  guess_num <- as.numeric(readline("Guess a number between 1 and 10 until you get it right : "))
}

break

The break statement can be used to terminate a loop.


Example:

Exit the loop if the number is equal to 15.

i <- 5
while (i <= 25) {
  print(i)
  if (i == 15) break
  i <- i + 5
}
## [1] 5
## [1] 10
## [1] 15