7 Loops

Loops repeat a block of code.

In this chapter, we discuss two loop types 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 display output.

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 that indicates 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 also write 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 sequence that matches the length of 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 2s occur in the vector rep(1:3, each = 2, times = 3).

vec <- rep(1:3, each = 2, times = 3)

count <- 0
for (i in seq_along(vec)) {
  if (vec[i] == 2) {
    count <- count + 1
  }
}

print(count)
## [1] 6

Notes:

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

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

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

i vec[i] TRUE count
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 would usually use the vectorized sum() instead. The loop above is useful because it shows how a for loop works; the vectorized version below is the approach you would typically use in practice.

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

Note: Wherever vectorized operations are possible, we should usually prefer them. In R, vectorized code is often shorter and faster than an equivalent for loop.


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 & n %% 5 == 0){ vec[n] <- "FizzBuzz" }
  else if (n %% 3 == 0){ vec[n] <- "Fizz" } 
  else if (n %% 5 == 0){ vec[n] <- "Buzz" } 
  else { vec[n] <- n }
}

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

Notes:

  • Create an empty vector.
  • Mark multiples of 3 and 5 as “FizzBuzz”.
  • Mark multiples of 3 as “Fizz”.
  • Mark multiples of 5 as “Buzz”.
  • Mark the remaining elements as the original numbers. The resulting vector is a character vector.

Solution 2: a better for loop

output <- vector()

for (i in 1:100) {
  output[i] <- ""
  if (i %% 3 == 0) {output[i] <- paste0(output[i], "Fizz")}
  if (i %% 5 == 0) {output[i] <- paste0(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] <- paste0(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] <- paste0(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

So far, each loop has used a single loop variable. Next we look at loops inside 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: "))

rows <- 1:m
cols <- 1:n
a <- matrix(m*n, nrow = m, ncol = n)
for (i in rows){
  for (j in cols){
    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

Exercise 5

Write an R program to guess a number between 1 to 10.

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

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