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.
## [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 output a result.
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:
vec
is 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 to indicate which element invalues
we 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 substitute
i in seq_along(values)
withi 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 value the same length as 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:
f
is the output container.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:
This example demonstrates a pattern of computation called a counter.
The variable
c
is initialized to 0 and then increments each time a 2 is found.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.
## [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:
- Determine whether the condition is true or false.
- If false, exit the
while
statement. - If true, run the body and go back to step 1.
Example:
## [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.
Reference: Rory Spanton. (2020). How to Solve the FizzBuzz Problem in R↩︎