7 Loops

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

In this chapter, we discuss two kinds of loops in R, for loops and while loops.

7.1 for loops

A for loop is used for iterating over items in a vector. 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. vector can be a vector, an array, or a list.

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

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

expression is often a grouped expression. In this case, the expressions must be surrounded by curly braces.

How it works:

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

For each variable in vector, expression is called once; the value of variable is then updated.

Example:

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
}

Compare the code above with the one below.

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

Exercise 1

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

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

vec <- numeric(0)
#vec <- vector("numeric", 0) returns a numeric vector of length 0
#vec <- c() returns a NULL object
#vec <- vector() returns a logical vector of length 0

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

Notes:

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

  2. seq_along(values) returns the index of each element in the vector values, which is a sequence 1,2,3,4,5,6. Each number in this sequence will be represented by i, the index to indicate which element in values we want to access.

  3. vec <- c(vec, values[i]) stores the outputs from the loop one by one.

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. seq_along(x) always returns a value the same length as x.

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

Exercise 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:

  1. f is the output container.

  2. i is the index.

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

Exercise 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:

  1. This example demonstrates a pattern of computation called a counter.

  2. The variable c is initialized to 0 and then increments each time a 2 is found.

  3. When the loop exits, c contains the result, which is the total number of 2’s.

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, however, you should just use the vectorized sum(). What we did above was just to show 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.


Exercise 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"   "Fizz" "13"   "14"   "Fizz"
##  [16] "16"   "17"   "Fizz" "19"   "Buzz" "Fizz" "22"   "23"   "Fizz" "Buzz" "26"   "Fizz" "28"   "29"   "Fizz"
##  [31] "31"   "32"   "Fizz" "34"   "Buzz" "Fizz" "37"   "38"   "Fizz" "Buzz" "41"   "Fizz" "43"   "44"   "Fizz"
##  [46] "46"   "47"   "Fizz" "49"   "Buzz" "Fizz" "52"   "53"   "Fizz" "Buzz" "56"   "Fizz" "58"   "59"   "Fizz"
##  [61] "61"   "62"   "Fizz" "64"   "Buzz" "Fizz" "67"   "68"   "Fizz" "Buzz" "71"   "Fizz" "73"   "74"   "Fizz"
##  [76] "76"   "77"   "Fizz" "79"   "Buzz" "Fizz" "82"   "83"   "Fizz" "Buzz" "86"   "Fizz" "88"   "89"   "Fizz"
##  [91] "91"   "92"   "Fizz" "94"   "Buzz" "Fizz" "97"   "98"   "Fizz" "Buzz"

Notes:

  • 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.

Solution 2: a better for loop 9

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)

Notes:

  • 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 <- as.integer(readline("Input the 1st number: "))
n <- as.integer(readline("Input the 2nd number: "))

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)

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

7.2 while loops

while loops repeat an expression while a condition is true. The goal of a while loop is that the condition eventually becomes false and that the loop terminates.

How it works:

while (condition) expression

The flow of execution for a while statement is:

  1. Determine whether the condition is true or false.
  2. If false, exit the while statement.
  3. If true, run the body and go back to step 1.

Example:

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. Therefore, we should use for wherever possible. It’s good practice to use the least flexible solution to a problem.

Exercise 5

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:10, 1)
guess_num <- 0

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

7.3 break

The break statement can be used to terminate a loop.

Example:

Exit the loop if the number is equal to 4.

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

Compare the example above with the one below where break goes to another place in the loop.

i <- 1
while (i < 6) {
  print(i)
  i <- i + 1
  if (i == 4) break
}
## [1] 1
## [1] 2
## [1] 3

  1. Reference: Rory Spanton. (2020). How to Solve the FizzBuzz Problem in R↩︎