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.
## [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:
## [1] 5
## [1] 10
## [1] 15
## [1] 20
## [1] 25
In loops, we must use the print() function if we want to display output.
Compare the code above with the one below.
## [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:
vecis the output container, which is a numeric vector. If we’re generating data, make sure that we set up an output container.seq_along(values)returns the index of each element in the vectorvalues, which is a sequence1,2,3,4,5,6. Each number in this sequence will be represented byi, the index that indicates which element invalueswe want to access.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 |
- In this case, we may also write
i in 1:length(values).
## [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.
## [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.
## 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.
## [1] 0 1 1 2 3 5 8 13 21 34
Notes:
fis the output container.iis 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:
This example demonstrates a pattern of computation called a counter.
The variable
countis initialized to 0 and then increments each time a 2 is found.When the loop exits,
countcontains 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.
## [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:
- Determine whether the condition is true or false.
- If false, exit the
whilestatement. - If true, run the body and go back to step 1.
Example:
## [1] 5
## [1] 10
## [1] 15
## [1] 20
## [1] 25