Control Flow: Conditional Statements and Iteration

Statistical Computing for Data Analysis

if() and else

Use if() and else to decide whether to evaluate one block of code or another, depending on a condition

x = 0.5

if (x >= 0) {
  x
} else {
  -x
}
## [1] 0.5

else if()

We can use else if() arbitrarily many times following an if() statement

x = -2

if (x^2 < 1) {
  x^2 
} else if (x >= 1) {
  2*x-1
} else {
 -2*x+1
}
## [1] 5

Another example:

today <- "Tuesday"
if (today == "Monday") {
  writeLines("Tell me a joke")
} else if (today == "Tuesday") {
  writeLines("Work work")
} else if (today == "Friday") {
  writeLines("Ready to break")
} else {
  writeLines("Have some fun")
}
## Work work

Quick decision making

In the ifelse() function we specify a condition, then a value if the condition holds, and a value if the condition fails

ifelse(x > 0, x, -x)
## [1] 2

Exactly equivalent to:

if (x > 0) {
  x
} else {
  -x
}
## [1] 2

One advantage of ifelse() is that it vectorizes nicely

Short-circuiting logical operations

We now look at && and ||, which are short-circuiting logical operations

u.vec = runif(10, -1, 1)
u.vec
##  [1]  0.88384946  0.66995706 -0.82529685 -0.97272948 -0.09800783  0.60526684
##  [7] -0.82985530  0.42879982  0.38784757 -0.75970069
u.vec[-0.5 <= u.vec & u.vec <= 0.5] = 999 ## evaluates elementwise
u.vec
##  [1]   0.8838495   0.6699571  -0.8252969  -0.9727295 999.0000000   0.6052668
##  [7]  -0.8298553 999.0000000 999.0000000  -0.7597007
# Safe guard using &&: If length(u.vec) > 0 is FALSE, R never evaluates min(u.vec)
if (length(u.vec) > 0 && min(u.vec) < 0) {
  print("Vector has at least one negative value.")
}
## [1] "Vector has at least one negative value."
u.vec
##  [1]   0.8838495   0.6699571  -0.8252969  -0.9727295 999.0000000   0.6052668
##  [7]  -0.8298553 999.0000000 999.0000000  -0.7597007
# Check whether we should skip further computation
if (any(u.vec > 0.9) || mean(u.vec) > 0.5) {
  print("Large values detected.")
}
## [1] "Large values detected."

Iteration

Why iteration matters

Computers excel at doing the same simple operation repeatedly without getting tired or distracted. Humans do not. Iteration is therefore one of the core ideas behind programming: we write a rule once, and let the computer apply it many times.

In R, there are several ways to express iteration. Choosing the right one affects clarity, correctness, and performance.

Main iteration paradigms in R:

We will start with explicit loops, because they make the logic of iteration transparent.

for() loops

A for() loop iterates a counter variable over a vector. On each iteration, the loop executes a block of code (the body) using the current value of the counter.

n <- 10
log.vec <- vector(length = n, mode = "numeric")

for (i in 1:n) {
  log.vec[i] <- log(i)
}

log.vec
##  [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
##  [8] 2.0794415 2.1972246 2.3025851

This pattern—initialize, iterate, update—is extremely common.

Exiting a loop early: break

Sometimes we want to stop iterating as soon as a condition is met. The break statement immediately exits the loop.

n <- 10
log.vec <- vector(length = n, mode = "numeric")

for (i in 1:n) {
  if (log(i) > 2) {
    cat("Stopping early: log(i) exceeded 2\n")
    break
  }
  log.vec[i] <- log(i)
}
## Stopping early: log(i) exceeded 2
log.vec
##  [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101
##  [8] 0.0000000 0.0000000 0.0000000

This is useful when:

Non-numeric counters

The counter variable does not have to be numeric. It simply takes values from a vector.

for (name in c("Mon", "Wed", "Fri")) {
  cat(name, "declined to comment\n")
}

This is often clearer than looping over indices.

An even safer version is to use seq_along.

x <- c(10, 20, 30)
out <- numeric(length(x))


for (i in seq_along(x)) {
out[i] <- log(x[i])
}

Nested loops

A loop body can itself contain another loop.

for (i in 1:4) {
  for (j in 1:(i^2)) {
    cat(j, " ")
  }
  cat("\n")
}
## 1  
## 1  2  3  4  
## 1  2  3  4  5  6  7  8  9  
## 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16

while() loops

A while() loop repeats its body as long as a condition remains true. The number of iterations is not fixed in advance.

i <- 1
log.vec <- c()

while (log(i) <= 2) {
  log.vec <- c(log.vec, log(i))
  i <- i + 1
}

log.vec
## [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101

Key features:

for() versus while()

Use a for() loop when: - the number of iterations is known in advance, or

Use a while() loop when: - you only know how to stop once you get there

Conceptually:

Takeaways