11.2 Loops
In addition to the conditional statements in Section 11.1, another important type of statements that controls the flow of programs is loops.
11.2.1 for
loops with known sequence
The first type of loops is to repeat a segment of codes with a known sequence. Let’s look at its syntax first.
The for
loop will sequentially assign each value of the val_seq
to val
and run the body of the loop.
For example, if you want to find the first ten values of the Fibonacci sequence. It is known that the first two values in a Fibonacci sequence is \(F_1 = 0\) and \(F_2 = 1\). Then, we have \(F_3 = F_1 + F_2 = 0 + 1 = 1\) and in general \(F_i = F_{i-2} + F_{i-1}\).
fib_seq <- rep(0, 10) #initialize the sequence
fib_seq[1] <- 0 #initialize the first element
fib_seq[2] <- 1 #initialize the second element
for (i in 3:10) {
# loop i from 3 to 10
fib_seq[i] <- fib_seq[i - 2] + fib_seq[i - 1] #compute the i-th element of fib_seq as the sum of the previous two elements
}
fib_seq #print the sequence
#> [1] 0 1 1 2 3 5 8 13 21 34
Here, we first initialize the desired sequence to be a zero vector of length 10. Then, initialize the first element to be 0 and second element of 1. After that, the for
loop is will initially set i
to the value of 3, and go over all the values in the sequence 3:10
. At the end, we get the length-10 Fibonacci sequence.
11.2.2 Combine the conditional statements with loops
In the body of the loop, you usually want to do different tasks depending on the conditions. In this case, you can add conditional statements in the body of the loop. Let’s print out all the numbers in fib_seq
and add the information whether it is even or odd, and count the number of even values and odd values.
count_even <- count_odd <- 0 #initialize the counts of even and odd values to be 0
for (fib_val in fib_seq) {
# go over all the values in fib_seq check if the current value is even
if (fib_val%%2 == 0) {
cat(fib_val, ": even\n") #if so, print the value
count_even <- count_even + 1 #then, add 1 to the even count
} else {
cat(fib_val, ": odd\n") #if not, print the value
count_odd <- count_odd + 1 #then, add 1 to the odd count
}
}
#> 0 : even
#> 1 : odd
#> 1 : odd
#> 2 : even
#> 3 : odd
#> 5 : odd
#> 8 : even
#> 13 : odd
#> 21 : odd
#> 34 : even
cat("There are ", count_even, " even values.\n") #print the count of even values
#> There are 4 even values.
cat("There are ", count_odd, " odd values.\n") #print the count of odd values
#> There are 6 odd values.
It is worth noting that for this task, we can actually avoid the loop using vectorize operations on the vector.
even_ind <- fib_seq%%2 == 0 #check whether each value is even
even_odd <- ifelse(even_ind, "even", "odd") #convert the logical vector to a character vector
cat(paste0(fib_seq, ": ", even_odd, "\n")) #create the string and print
#> 0: even
#> 1: odd
#> 1: odd
#> 2: even
#> 3: odd
#> 5: odd
#> 8: even
#> 13: odd
#> 21: odd
#> 34: even
cat("There are ", sum(even_ind), " even values.\n") #print the count of even values
#> There are 4 even values.
cat("There are ", sum(!even_ind), " odd values.\n") #print the count of odd values
#> There are 6 odd values.
11.2.3 Control Loop Flow With next
and break
Inside the if
and else
clause, you can use next
and break
to further control the flow.
The next
function goes directly to the next loop cycle, while break
jumped out of the current loop.
Let’s see an example of next
. For each value of the fib_seq
, if it is even, we will skip to the next value.
for (fib_val in fib_seq) {
# go over all the values in fib_seq check if the current value is even
if (fib_val%%2 == 0) {
next
}
cat(fib_val, ": odd\n") #if not, print the value
}
#> 1 : odd
#> 1 : odd
#> 3 : odd
#> 5 : odd
#> 13 : odd
#> 21 : odd
Now, suppose you want to find the first value in fib_seq
that is larger than 4. The break
can be used in this case.
for (fib_val in fib_seq) {
# go over all the values in fib_seq check if the current value is larger
# than 4
if (fib_val > 4) {
cat(fib_val, " is the first value larger than 4\n")
break
}
}
#> 5 is the first value larger than 4
cat(fib_val) #check the current fib_val
#> 5
You can see that the fib_val
is 5, indicating that we have breaked from the loop since it is larger than 4.
11.2.4 Nested Loops
In addition to a single loop, it is also common to put one loop inside another one, named the nested loop.
Let’s say we want to create a matrix A
of dimension \(5\times 5\) where each element \(A_{ij} = i+j\). To create such a matrix, we can write a nested loop over \(i\) and \(j\).
A <- matrix(0, 5, 5) #initialize the matrix A
for (i in 1:5) #loop over index i
for (j in 1:5){ #loop over index j
A[i, j] <- i + j #set the (i, j)-th element of A
}
A
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 2 3 4 5 6
#> [2,] 3 4 5 6 7
#> [3,] 4 5 6 7 8
#> [4,] 5 6 7 8 9
#> [5,] 6 7 8 9 10
Let’s try another example where we want to create the correlation matrix in the so-called AR(1) model. In particular, the corresponding matrix \(A\) is of dimension \(p\times p\) where \(A_{ij} = \rho^{|i-j|}\).
Let’s say an example of \(p = 4\) and \(\rho = 0.5\)
p <- 4
A <- matrix(0, p, p) #initialize the matrix A
rho <- 0.5
for (i in 1:p) #loop over index i
for (j in 1:p){ #loop over index j
A[i, j] <- rho^{abs(i - j)} #set the (i, j)-th element of A
}
A
#> [,1] [,2] [,3] [,4]
#> [1,] 1.000 0.50 0.25 0.125
#> [2,] 0.500 1.00 0.50 0.250
#> [3,] 0.250 0.50 1.00 0.500
#> [4,] 0.125 0.25 0.50 1.000
11.2.5 Nested Loop via the outer()
Function
It turns out for the two examples we showed in the last part, you can use avoid the loop by the outer()
function.
outer(1:5, 1:5, "+")
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] 2 3 4 5 6
#> [2,] 3 4 5 6 7
#> [3,] 4 5 6 7 8
#> [4,] 5 6 7 8 9
#> [5,] 6 7 8 9 10
outer(1:4, 1:4, function(i, j) {
0.5^{
abs(i - j)
}
})
#> [,1] [,2] [,3] [,4]
#> [1,] 1.000 0.50 0.25 0.125
#> [2,] 0.500 1.00 0.50 0.250
#> [3,] 0.250 0.50 1.00 0.500
#> [4,] 0.125 0.25 0.50 1.000
Let’s see another example of using outer()
.
outer(1:5, 1:5, function(x, y) {
paste(x, "+", y, "=", x + y)
})
#> [,1] [,2] [,3] [,4] [,5]
#> [1,] "1 + 1 = 2" "1 + 2 = 3" "1 + 3 = 4" "1 + 4 = 5" "1 + 5 = 6"
#> [2,] "2 + 1 = 3" "2 + 2 = 4" "2 + 3 = 5" "2 + 4 = 6" "2 + 5 = 7"
#> [3,] "3 + 1 = 4" "3 + 2 = 5" "3 + 3 = 6" "3 + 4 = 7" "3 + 5 = 8"
#> [4,] "4 + 1 = 5" "4 + 2 = 6" "4 + 3 = 7" "4 + 4 = 8" "4 + 5 = 9"
#> [5,] "5 + 1 = 6" "5 + 2 = 7" "5 + 3 = 8" "5 + 4 = 9" "5 + 5 = 10"
Finally, the outer()
function can also be applied on matrices, which will lead to a higher dimensional array.
11.2.6 Exercises
- Calculate Factorials
Write a for
loop that calculates the factorial of a given number n
.
The factorial of a number n
is the product of all integers from 1 to n
. For example, the factorial of 5 (5!) is 1 * 2 * 3 * 4 * 5 = 120
.
Example Input:
Expected Output:
"The factorial of 5 is 120."
- Sum of a Sequence
Use a for
loop to calculate the sum of all numbers from 1
to n
, where n
is a positive integer.
Example Input:
Expected Output:
"The sum of numbers from 1 to 10 is 55."
- Print Multiplication Table
Write a for
loop that prints the multiplication table of a given number x
up to 10.
Example Input:
Expected Output:
7 x 1 = 7
7 x 2 = 14
...
7 x 10 = 70
- Identify Prime Numbers
Write a program using nested for
loops that checks whether a given number n
is a prime number.
Print "n is a prime number"
if n
is prime, otherwise print "n is not a prime number"
.
Example Input:
Expected Output:
"13 is a prime number."