# Control Flow and Vectorisation — R Programming

Source: https://www.geekswithgeeks.com/en/r-programming/p-control

> Write conditions and loops, and prefer vectorised alternatives.

## When to loop and when not to

R has `if`/`else`, `for`, `while`, `repeat` with `break`, and `next` to skip an iteration. `if` expects a **single** TRUE or FALSE; passing a longer logical vector is an error in recent R versions, which is why vectorised work uses **`ifelse()`** or dplyr's `if_else()` and `case_when()` instead. Loops are fine for things that are genuinely sequential, such as simulations where each step depends on the previous one, reading files one by one, or calling an API per item. But for element-wise calculations, vectorised functions (`sum`, `cumsum`, arithmetic, comparisons) are much faster and clearer, because they run in compiled code. When you do write a loop that builds a result, **pre-allocate** the output (`results <- numeric(n)`) rather than growing a vector with `c()` each time, which copies it repeatedly. `seq_along(x)` is safer than `1:length(x)`, which misbehaves when `x` is empty (it gives `c(1, 0)`).

## Loop versus vectorised

A loop processes elements one at a time; a vectorised function processes the whole vector in one call.

![Top: a small arrow visiting each box of a row in turn. Bottom: one wide arrow covering the whole row at once.](assets/figures/r-programming/section-3-map.svg) — Figure 3.1 — Element-by-element loops versus vectorised operations.

## Loops done right, and vectorised alternatives

seq_along, pre-allocation and case_when.

```r
marks <- c(72, 45, 90, 38, 66)

# vectorised: preferred
grades <- dplyr::case_when(
  marks >= 85 ~ "A",
  marks >= 70 ~ "B",
  marks >= 40 ~ "C",
  .default = "Fail"
)

# loop with pre-allocation, when each step depends on the previous one
balance <- numeric(12)
balance[1] <- 10000
for (m in seq_along(balance)[-1]) {
  balance[m] <- balance[m - 1] * 1.01 + 500      # interest plus monthly deposit
}
round(tail(balance, 1))

# safe iteration over a possibly empty vector
empty <- c()
for (i in seq_along(empty)) print(i)             # runs zero times
# for (i in 1:length(empty)) ...                 # runs for i = 1 and i = 0: a bug

if (mean(marks) > 60) {
  message("Class average is above 60")
} else {
  message("Class needs support")
}
```

## if needs one TRUE or FALSE

`if (marks > 40)` with a vector of marks is an error. Decide whether you mean "for each element" (use `ifelse`/`case_when`) or "any" or "all" (use `any()` or `all()`).

**Quiz:** Why is seq_along(x) preferred over 1:length(x) in loops?

- [x] It correctly produces an empty sequence when x is empty
- [ ] It is faster for long vectors
- [ ] It sorts x
- [ ] It works only with lists

*Answer:* It correctly produces an empty sequence when x is empty. 1:length(x) gives c(1, 0) for an empty x, causing a loop to run when it should not.
