# 5 Control flow

## 5.1 Choices

1. Q: What type of vector does each of the following calls to ifelse() return?

ifelse(TRUE, 1, "no")
ifelse(FALSE, 1, "no")
ifelse(NA, 1, "no")

Read the documentation and write down the rules in your own words.

A: The arguments of ifelse() are test, yes and no. ifelse() operates elementwise, so when yes or no are shorter than test, they will be recycled. (When they are longer than test, their additional elements will be ignored.)

The function returns the entry for yes (when test is TRUE), no (when test is FALSE) or NA (when test is NA). The final object is coerced according to usual coercion rules with attributes (including the class) being stripped. Therefore, the expressions above return vectors of type double, character and logical.

2. Q: Why does the following code work?

x <- 1:10
if (length(x)) "not empty" else "empty"
#> [1] "not empty"

x <- numeric()
if (length(x)) "not empty" else "empty"
#> [1] "empty"

A: if() expects a logical condition. Objects of other types are coerced to logical if possible. 0 is coerced to FALSE while other numeric input is coerced to TRUE. Numeric input which is not logically interpretable by if(), like NaN, NA_integer and NA_real, will lead to an error.

## 5.2 Loops

1. Q: Why does this code succeed without errors or warnings?

x <- numeric()
out <- vector("list", length(x))
for (i in 1:length(x)) {
out[i] <- x[i] ^ 2
}
out

A: The vector we are iterating over are of length 0. Our loop goes from i = 1 to i = 0. As we use [<- and [ for indexing, we need to be aware of their subsetting behaviour for out-of-bounds and zero indices.

During the first iteration x[1] will generate an NA (out-of-bounds indexing for atomics). The resulting NA (from squaring) will be assigned to the empty length-1 list out[1] (out-of-bounds indexing for lists).

In the next iteration, x[0] will return numeric(0) (zero indexing for atomics). Again squaring doesn’t change the value and numeric(0) is assigned to out[0] (zero indexing for lists). Please be aware, that out doesn’t change during the last iteration.

Overall the code works, because each step includes valid R operations. (Though it may have been very helpful to warn the user about the unusual inputs to this loop.)

2. Q: What does the following code tell you about when the vector being iterated over is evaluated?

xs <- c(1, 2, 3)
for (x in xs) {
xs <- c(xs, x * 2)
}
xs
#> [1] 1 2 3 2 4 6

A: In this loop x takes on the values of the initial xs (1, 2 and 3), which evaluated just once in the beginning of the loop.

3. Q: What does the following code tell you about when the index is updated?

for (i in 1:3) {
i <- i * 2
print(i)
}
#> [1] 2
#> [1] 4
#> [1] 6

A: In a for-loop the index is updated in the beginning of each iteration. Therefore, reassigning the index symbol during one iteration doesn’t affect the following iterations.