R code

library("tidyverse")
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.4.0      ✔ purrr   0.3.5 
## ✔ tibble  3.1.8      ✔ dplyr   1.0.10
## ✔ tidyr   1.2.1      ✔ stringr 1.4.1 
## ✔ readr   2.1.3      ✔ forcats 0.5.2 
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()

Control structures in R provide conditional flow as well as looping. An R expression is evaluated within the loop or as the result of a conditional statement.

The following are example R expressions

# First expression
1+2
## [1] 3
# Second expression
a <- 1; b <- 2; a+b
## [1] 3
# Third expression
{
  a <- 1
  b <- 2
  a + b
}
## [1] 3

See

?expression

Conditionals

Conditionals include if-else type statements

if

if (TRUE) {
  print("This was true!")
}
## [1] "This was true!"
this <- TRUE
if (this) {
  print("`this` was true!")
}
## [1] "`this` was true!"
if (1<2) {
  print("one is less than two!")
}
## [1] "one is less than two!"
if (1>2) {
  print("one is greater than two!")
}

Using variables

a <- 1
b <- 2
if (a < 2) {
  print("`a` is less than 2!")
}
## [1] "`a` is less than 2!"
if (a < b) {
  print("`a` is less than `b`!")
}
## [1] "`a` is less than `b`!"

if-else

if (a < b) {
  print("`a` is less than `b`!")
} else {
  print("`b` is not less than `a`!")
}
## [1] "`a` is less than `b`!"
if (a > b) {
  print("`a` is greater than `b`!")
} else {
  print("`a` is not greater than `b`!")
}
## [1] "`a` is not greater than `b`!"
if (a > b) {
  print("`a` is greater than `b`!")
} else if (dplyr::near(a,b)) {
  print("`a` is near `b`!")
} else {
  print("`a` must be greater than b")
}
## [1] "`a` must be greater than b"

You can omit the brackets { } for single line expressions

if (a < b)
  print("`a` is less than `b`!")
## [1] "`a` is less than `b`!"

ifelse

The ifelse() function takes a logical vector as a first argument and then two scalars.

ifelse(c(TRUE, FALSE, TRUE), "this was true", "this was false")
## [1] "this was true"  "this was false" "this was true"

switch

A rarely used function is switch() which implements a case-switch comparison.

this <- "a"
switch(this,
       a = "`this` is `a`",
       b = "`this` is `b`",
       "`this` is not `a` or `b`")
## [1] "`this` is `a`"
this <- "b"
switch(this,
       a = "`this` is `a`",
       b = "`this` is `b`",
       "`this` is not `a` or `b`")
## [1] "`this` is `b`"
this <- "c"
switch(this,
       a = "`this` is `a`",
       b = "`this` is `b`",
       "`this` is not `a` or `b`")
## [1] "`this` is not `a` or `b`"

Loops

There are 3 base types of loops: for, while, and repeat.

for

These loops use the reserved words for and in,

Iterating over integers

The most common use of a for loop is to loop over integers.

for (i  in 1:10) {
  print(i)
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

Combine with control structures

for (i  in 1:10) {
  if (i > 5)
    print(i)
}
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

Iterate over non-integers

Can also iterate over non-integers

for (d in c(2.3, 3.5, 4.6)) {
  print(d)
}
## [1] 2.3
## [1] 3.5
## [1] 4.6
for (c in c("my","char","vector")) {
  print(c)
}
## [1] "my"
## [1] "char"
## [1] "vector"

While these are possible, I find I rarely use them.

seq_along()

Be careful when iterating over objects that a potentially NULL.

this <- NULL
for (i in 1:length(this)) {
  print(i)
}
## [1] 1
## [1] 0

Since this had no length, you probably didn’t want to enter the for loop at all. To be safe, you can use seq_along().

for (i in seq_along(this)) {
  print(i)
}
my_chars <- c("my","char","vector")
for (i in seq_along(my_chars)) {
  print(paste(i, ":", my_chars[i]))
}
## [1] "1 : my"
## [1] "2 : char"
## [1] "3 : vector"

seq_len()

For data.frames use seq_len() with nrow().

for (i in seq_len(nrow(ToothGrowth))) {
  if (ToothGrowth$supp[i] == "OJ" & 
      near(ToothGrowth$dose[i], 2) &
      ToothGrowth$len[i] > 25) {
    print(ToothGrowth[i,])
  }
}
##     len supp dose
## 51 25.5   OJ    2
##     len supp dose
## 52 26.4   OJ    2
##     len supp dose
## 56 30.9   OJ    2
##     len supp dose
## 57 26.4   OJ    2
##     len supp dose
## 58 27.3   OJ    2
##     len supp dose
## 59 29.4   OJ    2

Like with if and else statements, for loops can omit the brackets { } for single line expressions.

for (i in 1:10) 
  print(i)
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10

while

The while() loop can be used to construct while loops.

a <- TRUE
while (a) {
  print(a)
  a <- FALSE
}
## [1] TRUE
i <- 0
while (i < 10) {
  print(i)
  i <- i + 1
}
## [1] 0
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
x <- 2
while (x < 1) { # Evaluated before the loop
  print("We entered the loop.")
}

while (x < 100) { # Evaluated after each loop
  x <- x*x
  print(x)
}
## [1] 4
## [1] 16
## [1] 256

Be careful about infinite loops

while (TRUE) {
  # do something
}

You can typically Escape out of infinite loops. Often, you will want to make sure the infinite loop never occurs.

max_iterations <- 1000
i <- 1
while (TRUE & (i < max_iterations) ) {
  i <- i + 1
  # Do something
}
print(i)
## [1] 1000

repeat

An alternative to while() is repeat combined with break.

i <- 10
repeat {
  print(i)
  i <- i + 1
  if (i > 13)
    break
}
## [1] 10
## [1] 11
## [1] 12
## [1] 13
i <- 1
repeat {
  print(i)
  i <- i + 1
  if (i %% 2) { # %% is the mod function, 0 is FALSE and 1 is TRUE
    next        # skips to next iteration of repeat
  }
  if (i > 14)
    break
}
## [1] 1
## [1] 2
## [1] 3
## [1] 4
## [1] 5
## [1] 6
## [1] 7
## [1] 8
## [1] 9
## [1] 10
## [1] 11
## [1] 12
## [1] 13
## [1] 14
## [1] 15