# 9 Control Flow

This chapter was developed from scratch for the Fall 2022 semester. As such, you might notice a few extra typos, or some topics that are not well explained. If you encounter these issues, please let us know on the discussion forum. Except some additional changes to what is currently published while this warning persists.

So far, all of the code we’ve written contained a sequence of R expressions that were to be evaluated in order. While this type of code can still be useful, by introducing **control flow**^{1}, we can use code to more interestingly define when and how many times various expressions should be evaluated.

After reading this chapter you should be able to:

*Use*`if`

and`else`

statements to conditionally evaluate R expressions.*Use*`ifelse`

as a vectorized alternative to`if`

and`else`

statements.

## 9.1 `if`

First, for documentation on control flow in R, use:

`::Control ?base`

We’ll start with the simplest and most important control flow constructs, the `if`

statement, which allows for conditional evaluation of R expressions. The general syntax is given by:

```
if (condition) {
code_to_evaluate }
```

First, note that `if`

is *not* a function, and we place a space between `if`

and the `(`

that follows to make this clear. Inside those parentheses, the `condition`

determines whether or not to evaluate `code_to_evaluate`

. In particular, `condition`

must be an R expression that evaluates to a length one logical vector containing either TRUE or FALSE. Much like functions, the “body” of the `if`

statement is contained within curly braces, `{}`

. This is not strictly necessary, and can be avoided for one-line statements, but we recommend that beginners always use the braces to clarify exactly which expressions will be conditionally evaluated.^{2}

```
if (TRUE) {
print("Hello, World!") # evaluates because condition is TRUE
}
```

`#> [1] "Hello, World!"`

```
if (FALSE) {
print("Hello, World!") # does not evaluate because condition is TRUE
}
```

Obviously, using `TRUE`

or `FALSE`

for the input to the condition is not particularly useful. Instead, using code the evaluates to `TRUE`

or `FALSE`

will be much more common.

```
= "bar"
foo if (is.character(foo)) {
print(foo)
}
```

`#> [1] "bar"`

```
= 42
baz if (is.character(baz)) {
print(baz)
}
```

Note that if the condition is not a logical value, R will attempt to coerce it to be logical.

```
if (42) {
print("Hello, World!")
}
```

`#> [1] "Hello, World!"`

As we’ve seen previously, sometimes this is not possible.

```
if ("foo") {
print("Hello, World!")
}
```

`#> Error in if ("foo") {: argument is not interpretable as logical`

Be aware that the condition *must* be length one. Conditions greater than length one will result in an error.

```
if (c(TRUE, FALSE)) {
42
}
```

`#> Error in if (c(TRUE, FALSE)) {: the condition has length > 1`

## 9.2 `else`

Consider the following code:

```
= 42
x if (x > 100) {
print("x is greater than 100")
}if (x <= 100) {
print("x is less than or equal to 100")
}
```

`#> [1] "x is less than or equal to 100"`

This is valid code, however, it might not be the best way to write it. We can improve this code by adding an `else`

statement.

```
= 42
x if (x > 100) {
print("x is greater than 100")
else {
} print("x is less than or equal to 100")
}
```

`#> [1] "x is less than or equal to 100"`

You’ll probably first notice that this code looks similar to the previous code. The benefit to using `else`

here is that we do no need to determine what the complement of `x > 100`

is, to ensure that the body of only one of the `if`

statements is run. In this case it is obvious, but in more complex situations, determine the complement expression could be non-trivial.

While `if`

can be used without `else`

, an `else`

usage must follow an `if`

usage. The general syntax is:

```
if (condition) {
# run this code if the condition evaluates to TRUE
else {
} # run this code if the condition evaluates to FALSE
}
```

It is also possible to chain together multiple `if`

and `else`

statements.

```
# setup data
= 22
m = 0
n
# series of if-else statements
if (m < 20) {
= 20 # will run if m < 20
n else if (m < 40) {
} = 40 # will run if 20 <= m < 40
n else if (m < 60) {
} = 60 # will run if 40 <= m < 60
n else {
} = Inf # will run if 60 <= m
n
}
# check "result"
n
```

`#> [1] 40`

One “trick” to be aware of, R will evaluate the code in the body of the **first** condition met. For example `22 < 40`

and `22 < 60`

will both evaluate to `TRUE`

, but `22 < 40`

occurs first, thus `n = 40`

is evaluated, and the remainder to the chain is ignored.

## 9.3 `ifelse`

Knowing that R is extremely vector-focused, `if`

statements might feel like a bit of an outlier given that the condition must evaluate to a *single* `TRUE`

or `FALSE`

value, that is, a logical vector of length one.

Thankfully, R includes the `ifelse`

function, which can be thought of as a “vectorized” version of the usual `if`

and `else`

construction. For details, use:

`::ifelse ?base`

First let’s translate the following `if`

and `else`

code to an `ifelse`

usage.

```
if (42 > 2) {
"!"
else {
} "?"
}
```

`#> [1] "!"`

`ifelse(test = 42 > 2, yes = "!", no = "?")`

`#> [1] "!"`

Simple enough, but that was a terribly boring example, because `42 > 2`

evaluates to `TRUE`

, and thus is a logical vector of length one. The following example will better demonstrate the power of the `ifelse`

function.

`ifelse(1:10 %% 2 == 0, "even", "odd")`

`#> [1] "odd" "even" "odd" "even" "odd" "even" "odd" "even" "odd" "even"`

First, note that we are not using the argument names in this example, as often, that will be how you see `ifelse`

examples presented.

Next, let’s look at the result of evaluating the value passed to the `test`

argument.

`1:10 %% 2 == 0`

`#> [1] FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE FALSE TRUE`

So, it *appears* that the `ifelse`

function takes as input a logical vector, potentially of length greater than one, and returns a vector with the `yes`

argument’s value (`"even"`

) in place of any `TRUE`

values, and the `no`

argument’s value (`"odd"`

) in place of any `FALSE`

value.

Hopefully, you’re already yelling: “But there is actually vector recycling taking place!”

R will recycle the `yes`

and `no`

inputs to have the same length as the `test`

. If either of `yes`

or `no`

is longer than `test`

, any element at an index beyond the length of `test`

is ignored.

Use what you’ve already learned about recycling to reason through the following example.

```
= c(TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, TRUE)
test_ex = c(0, 1)
yes_ex = c(2, 4, 6)
no_ex ifelse(test_ex, yes_ex, no_ex)
```

`#> [1] 0 4 0 1 4 6 0 1`

## 9.4 Loops

Loops (`for`

, `while`

, and `repeat`

) are another form of control flow. We will put off discussing these until next chapter so that we can first introduce the apply functions as a more common R approach to performing the types of operations usually done in other languages with loops.

## 9.5 Summary

- TODO: You’ve learned to…

## 9.6 What’s Next?

- TODO: iteration

## 9.7 TODO

- TODO: see also: Hands-On Programming with R: else Statements
- TODO: we used print here a lot, that doesn’t mean that you should.
- TODO:
`switch`

Control flow is a defining characteristic of imperative programming.↩︎

This will also be benefical when we also add

`else`

statements.↩︎