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
loopsA 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:
vec
is our output numeric vector. If we’re generating data, make sure that we set up the output container.
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.
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.
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 |
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:
""
.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”.if (i %% 5 == 0) {output[i] <- paste(output[i], "Buzz")}
is evaluated, which works in the same way as the first conditional statement.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
loopsfor
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
next
The next
statement can be used to discontinue one cycle and skip to the “next” cycle.
Example:
Print all the numbers from 0 to 6 except 3 and 6.
num <- 0:6
for (i in num) {
if (i == 3 | i ==6) {
next
}
print(i)
}
## [1] 0
## [1] 1
## [1] 2
## [1] 4
## [1] 5