10.2 Loops
In addition to the conditional statements in Section 10.1, another important type of statements that controls the flow of programs is loops.
10.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.
for (val in val_seq){
statement1
statement2 }
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}\).
<- rep(0,10) #initialize the sequence
fib_seq 1] <- 0 #initialize the first element
fib_seq[2] <- 1 #initialize the second element
fib_seq[for(i in 3:10){ #loop i from 3 to 10
<- 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[i]
}#print the sequence
fib_seq #> [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.
10.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_odd <- 0 #initialize the counts of even and odd values to be 0
count_even for(fib_val in fib_seq){ #go over all the values in fib_seq
if(fib_val %% 2 == 0){ #check if the current value is even
cat(fib_val, ": even\n") #if so, print the value
<- count_even + 1 #then, add 1 to the even count
count_even else{
} cat(fib_val, ": odd\n") #if not, print the value
<- count_odd + 1 #then, add 1 to the odd count
count_odd
}
}#> 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.
<- fib_seq %% 2 == 0 #check whether each value is even
even_ind <- ifelse(even_ind, "even", "odd") #convert the logical vector to a character vector
even_odd 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.
10.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
if(fib_val %% 2 == 0){ #check if the current value is even
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
if(fib_val > 4){ #check if the current value is larger than 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.
10.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\).
<- matrix(0, 5, 5) #initialize the matrix A
A for (i in 1:5) #loop over index i
for (j in 1:5){ #loop over index j
<- i + j #set the (i, j)-th element of A
A[i, j]
}
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\)
<- 4
p <- matrix(0, p, p) #initialize the matrix A
A <- 0.5
rho for (i in 1:p) #loop over index i
for (j in 1:p){ #loop over index j
<- rho^{abs(i - j)} #set the (i, j)-th element of A
A[i, j]
}
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
10.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.
<- matrix(1:4, 2, 2)
A outer(A, A, "-") #a 4-dimensional array.
#> , , 1, 1
#>
#> [,1] [,2]
#> [1,] 0 2
#> [2,] 1 3
#>
#> , , 2, 1
#>
#> [,1] [,2]
#> [1,] -1 1
#> [2,] 0 2
#>
#> , , 1, 2
#>
#> [,1] [,2]
#> [1,] -2 0
#> [2,] -1 1
#>
#> , , 2, 2
#>
#> [,1] [,2]
#> [1,] -3 -1
#> [2,] -2 0