Previous chapter
Writing FunctionsControl Flow
Next chapter

## if and else

### Control Flow

Control flow refers to the order in which a function executes the statements in its body of code.

By default, R functions will execute each line of code in the body in order, and then return the result of the last line of code. But it doesn’t have to be this way. You can write functions that run some code in some situations and other code in other situations.

Let’s learn how!

### if

Take a look at the code below. What is happening?

``x <- 1``
``x == 1``
``## [1] TRUE``
``x > 1``
``## [1] FALSE``
``log(x)``
``## [1] 0``
``if (x == 1) log(x)``
``## [1] 0``
``if (x > 1) log(x)``

Try to describe what `if` is doing in your own words. Your best guess is okay!

### Data cleaner

Think you have it? Let’s check. We’ll use `if` to write a useful function.

Many data sets use their own symbols to represent missing values. For example, NOAA will often use -99 to represent missing values in weather data sets. Let’s write a function that checks whether a value is -99, and if so replaces the value with NA, like this:

``clean(1)``
``## [1] 1``
``clean(-99)``
``## [1] NA``

Here is a start. `clean()` takes an object and returns the object, but `clean()` is missing an important piece of code.

• Add an `if` statement to the beginning of `clean()`. Your statement should assign NA to x if x equals -99. Then click Submit Answer.
``x <- -99``
``````clean <- function(x) {
# add if statement here
x
}``````
``"Don't forget to use == to check for equality."``
``````clean <- function(x) {
if (x == -99) x <- NA
x
}``````
``strict_check("clean() replaces x with NA if x is -99, otherwise clean() returns x as is.")``

### else

Here is a second version of `clean()` that uses on a new command. Can you tell what `else` does?

``````clean <- function(x) {
if (x == -99) NA else x
}``````
• Run `clean()` with several different values, `22`, `-99`, `3`. What does `clean()` return in each case? Why?
``````clean <- function(x) {
if (x == -99) NA else x
}``````

### A word about syntax

Although you can put `if` and `else` on the same line, you shouldn’t because it is easy for readers to miss the trailing `else` when they scan the code. Also, placing `if` and `else` on the same line can make very long lines.

It would be more common to write our function like this:

``````clean <- function(x) {
if (x == -99) NA
else x
}``````

R parses the `if` and `else` lines as a single statement as long as `else` is the first thing that follows the `if` statement. As a result, R will return the result of the combined if else statement if it appears at the end of a function.

### {}

You can also pass `if` and `else` chunks of code surrounded by braces, `{}`. Braces group multiple lines of code into a single “piece.” When you use braces in an if else statement, R will run (or not run) everything between the braces.

In this example, R will run all three lines that follow `else` whenever x does not equal -99.

``````clean <- function(x) {
if (x == -99) {
NA
} else {
x <- x^2
x <- sqrt(x)
x
}
}``````

When you use braces, indent everything between the braces by two spaces to make your code more readable. And of course, you can use braces to organize your code even if you have a single line of code between the braces.

### If else quiz

SAS often saves missing values as `"."`.

• Write a function named `clean2` that takes a value named `x` and returns an NA if the value is `"."` (and returns the value of `x` otherwise).
``x <- "."``
``"clean2() should closely resemble clean()."``
``````clean <- function(x) {
if (x == ".") NA
else x
}``````
``strict_check("What if you'd like to check for both -99 and . in the same function?")``

### else if

In that case, you can use `else` to chain together multiple `if` statements.

``````clean <- function(x) {
if (x == -99) NA
else if (x == ".") NA
else x
}``````

This does the same thing as

``````clean <- function(x) {
if (x == -99) NA
else {
if (x == ".") NA
else x
}
}``````

`clean()` will:

1. Check whether `x == -99`. If so `clean()` will return NA and skip the rest of the code. If not, `clean()` will…
2. Check whether `x == "."`. If so, `clean()` will return NA and skip the rest of the code. If not, `clean()` will…
3. Evaluate `x` and return its value.

`else if` is more readable than nested if else statements, especially if you use many `else if`s.

You can use `else` to string together as many if statements as you like. R will treat the result as a single multi-part if else statement. Be thoughtful with the order. R will always evaluate the clauses in order, executing the code in the first clause whose condition is true and ignoring every clause after that.

• Write a function named `clean()` that uses `if`, `else`, and `else if` statements to replace the following four values with NA before returning x, `-99`, `"."`, `""`, `"NaN"`. Then click Submit Answer.
``x <- ""``
``````clean <- function(x) {
if (x == -99) NA
else if (x == ".") NA
else if (x == "") NA
else if (x == "NaN") NA
else x
}``````
``strict_check("You can add as many else if's after if as you like. However, you cannot add an else if clause after an else because R will interpret the else as the end of a complete if else statement.")``

### Quiz

``````foo <- function(x) {
if (x > 2) "a"
else if (x < 2) "b"
else if (x == 1) "c"
else "d"
}
foo(1)``````

### Quiz

``````clean <- function(x) {
if (x == -99) NA
if (x == ".") NA
if (x == "NaN") NA
x
}
clean(-99)``````

### Congratulations!

You now know how to use `if` and `else` in your code. Let’s look at another way to control flow in R.

## return() and stop()

You can tell R to stop executing a function early with

• `return()`
• `stop()`, and
• `stopifnot()`

Each will work only in the context of a function (because they stop the function). You wouldn’t run these directly at the command line, but they provide a powerful way to control the flow of your functions: They can make if else statements unnecessary and they can even make your code less buggy.

### return()

When R encounters `return()` it will stop executing the function that called `return()`. If you pass a value to `return()`, R will return that value when it stops executing the function. Let’s see how it works.

``````impatient_square <- function(x) {
return(x)
x^2
}``````

### R with a python accent

If you are a python user, you might already use `return()` …unnecessarily. In python, you explicitly tell each function what to return, e.g.

``````def mysquare(x):
y = x * x
return y``````

Translated to R this becomes:

``````my_square <- function(x) {
y <- sum(x) / length(x)
return(y)
}``````

But in R, this `return()` is not needed. R functions automatically return the result of their last line of code. In R, you can save `return()` for unusual control flow.

### Using return()

Remember this function? It didn’t work as expected because we forgot to link our if statements with `else`.

• Fix the function not by adding `else`, but by adding `return()` in the right places. Then Click Submit Answer.
``````clean <- function(x) {
if (x == -99) NA
if (x == ".") NA
if (x == "NaN") NA
x
}``````
``````clean <- function(x) {
if (x == -99) return(NA)
if (x == ".") return(NA)
if (x == "NaN") return(NA)
x
}``````
``strict_check("This version is slightly easier to read than a linked if else tree because you can think about each if clause separately. You can often avoid long, nested if else trees by using return() thoughtfully.")``

### NULL

`clean()` is a fairly useful function, but it does have one flaw.

• What happens when `x = NULL`? Run the code and find out.
``````clean <- function(x) {
if (x == -99) return(NA)
if (x == ".") return(NA)
if (x == "NaN") return(NA)
x
}
clean(NULL)``````
``````clean <- function(x) {
if (x == -99) return(NA)
if (x == ".") return(NA)
if (x == "NaN") return(NA)
x
}
clean(NULL)``````

`clean()` cannot handle `NULL` because `if` returns an error when it evaluates `NULL == -99`. And, unfortunately, the error message isn’t very clear. This is the perfect case for `stop()`.

``clean(NULL)``
``## Error in if (x == -99) return(NA): argument is of length zero``

### stop()

`stop()` behaves like `return()`, but instead of returning a value, `stop()` returns an error, complete with a custom error message. Can you tell how it works?

``````immovable_square <- function(x) {
stop("I refuse to proceed.")
x^2
}``````

### Use stop()

• Use `if` and `is.null()` to add a `stop()` call at the beginning of `clean()`. The command should return the error message `"x is NULL"` whenever x is NULL.
• Then click Submit Answer.
``````clean <- function(x) {
if (x == -99) return(NA)
if (x == ".") return(NA)
if (x == "NaN") return(NA)
x
}``````
``````clean <- function(x) {
if (is.null(x)) stop("x is NULL")
if (x == -99) return(NA)
if (x == ".") return(NA)
if (x == "NaN") return(NA)
x
}``````
``strict_check("Now `clean()` can handle NULL values in an intelligent way. This pattern (if + stop) is so common that R provides a shortcut for it, `stopifnot()`.")``

### stopifnot()

`stopifnot()` is a more readable substitute for statements that combine `if` and `stop()`. Can you guess how it works?

### differences

`stopifnot()` is different from `if` + `stop()` in a few important ways:

1. Instead of checking whether a condition is met, `stopifnot()` checks whether a condition is not met.
2. `stopifnot()` does not pass along a custom error message. Instead, `stopifnot()` always explains that the condition was not true:

``````x <- -1
stopifnot(x >= 0)``````
``## Error: x >= 0 is not TRUE``

Notice that the first argument of `stopifnot()` should always be a logical condition, the inverse of the condition it replaces in an `if` + `stop()` statement.

You can include additional logical conditions for `stopifnot()` to check after the first. Separate each with a comma.

Think you have it?

• Try replacing the `if` + `stop()` statement in `clean()` with `stopifnot()`. Then click Submit Answer.
``````clean <- function(x) {
if (is.null(x)) stop("x is NULL")
if (x == -99) return(NA)
if (x == ".") return(NA)
if (x == "NaN") return(NA)
x
}``````
``"You can reverse the result of `is.null()` by placing an `!` in front of it: `!is.null()`."``
``````clean <- function(x) {
stopifnot(!is.null(x))
if (x == -99) return(NA)
if (x == ".") return(NA)
if (x == "NaN") return(NA)
x
}``````
``strict_check("stopifnot() is both very readable and very useful. Click Continue to see why.")``

### Defensive programming with stopifnot()

You can save yourself debugging time by writing your functions to fail fast with clear error messages. To do this, think about situations that will lead to errors and then check for them with `stopifnot()` at the beginning of your code.

``````clean <- function(x) {
stopifnot(!is.null(x), is.numeric(x), length(x) == 1)

if (x == -99) return(NA)
x
}``````

If things go wrong, `stopifnot()` will help you see what you need to fix as soon as you run your function. Compare this to what will happen if you do not use `stopifnot()`:

1. Your code will run until it triggers a (perhaps unhelpful) error message
2. Your code may not trigger an error message, but return an incorrect result that you will think is true. This would very bad.

Congratulations! You know two techniques of control flow, how to:

1. Run specific code in specific cases
2. Stop execution early

In the Advanced Control Flow tutorial, you’ll learn how to combine logical tests in an if statement as well as how to write if statements that work with vectors, which is a prerequisite if you want to write vectorized functions.