## 4.3 Repeating Things: Looping

We have looked a bit into the aspect of flow control that pertains to making decisions. Let us now turn to the R-constructs that make the computer repeat actions.

### 4.3.1 For Loops

The reserved word `for`

is used to make R repeat an action a specified number of times.

We begin with an very simple example:

```
## Hello, Dorothy!
## Hello, Dorothy!
## Hello, Dorothy!
## Hello, Dorothy!
```

Here’s how the loop works. Recall that the vector `1:4`

is simply the sequence of integers from 1 to 4:

`## [1] 1 2 3 4`

When R sees the code `(i in 1:4)`

it knows that it will have to go four times through the body of the loop.

(The *body* of a loop is what’s contained in the brackets after `for(i in 1:4)`

). At the start of the loop, the *index variable* `i`

is set to 1. After the body is executed the first time, R sets `i`

to 2, then executes the body again. Then R sets `i`

to 3 and executes the body yet again. Then R sets `i`

to 4, and executes the body for the final time. The result is four lines printed out to the console.

The more you need to repeat a particular pattern, the more it makes sense to write your code with a loop.

The general form of a loop is:

`var`

is the index variable, and it can be any permitted name for a variable, and `seq`

can be any vector. As R traverses the loop, the value of the index variable `var`

becomes each element of the vector `seq`

in turn. With every change in the value of `var`

, the code in the brackets is executed.

To *iterate* is to do a thing again and again. The vector `seq`

is sometimes called an *iterable*, since it is “iterated over.” It contains the values that the index variable will assume, one by one, as the loop is repeated.

It’s important to realize that the index variable can have *any* valid name, and the sequence can be any type of vector at all—not just a sequence of consecutive whole numbers beginning with 1. This level of generality permits a for-loop to automate a wide variety of tasks, and for its code to be written in a way that evokes the operations being performed.

For example, here is a loop to print out some greetings;

```
people <- c("Dorothy", "Tin Man", "Scarecrow", "Lion")
for ( person in people ) {
cat("Hello, ", person, "!\n", sep = "")
}
```

```
## Hello, Dorothy!
## Hello, Tin Man!
## Hello, Scarecrow!
## Hello, Lion!
```

Or perhaps you want to abbreviate a vector-ful of weekday-names:

```
weekdays <- c("Saturday", "Monday", "Friday", "Saturday")
for ( day in weekdays ) {
print(abbrDay(day))
}
```

```
## [1] "Sat"
## [1] "Mon"
## [1] "Fri"
## [1] "Sat"
```

Quite often you will want to store the results of your trips through the loop. Let’s do this for our abbreviated weekdays:

We used the `character()`

function to create the character vector `abbrDays`

. We specified that the number of elements in `abbrDays`

shall be the same as the number of elements in `weekdays`

. Right now `abbrDays`

isn’t very interesting, as all of its elements are empty strings:

`## [1] "" "" "" ""`

We will now write a for-loop to fill it up with abbreviations:

Now each of the four elements of `abbrDays`

contains the abbreviation for the corresponding element of `weekdays`

:

`## [1] "Sat" "Mon" "Fri" "Sat"`

You will often have reason to set up an empty vector of a definite length and use a loop to store information in it. The general format looks like this:

### 4.3.2 For-Loop Caution

You might need to exercise some caution in your choice name for the index variable. If you are already using it as the name for a variable in the same environment, then you will overwrite that variable, as demonstrated by the following code:

`## Today is Thursday.`

```
weekdays <- c("Saturday", "Monday", "Friday", "Saturday")
abbrDays <- character(length(weekdays))
for ( day in weekdays ) {
print(day)
}
```

```
## [1] "Saturday"
## [1] "Monday"
## [1] "Friday"
## [1] "Saturday"
```

`## Today is Saturday.`

Of course, that won’t happen if the for-loop is inside of a function and your variable is outside of it.

`## Today is Thursday.`

```
weekdays <- c("Saturday", "Monday", "Friday", "Saturday")
listDays <- function(days) {
for ( day in days ) {
print(day)
}
}
listDays(weekdays)
```

```
## [1] "Saturday"
## [1] "Monday"
## [1] "Friday"
## [1] "Saturday"
```

`## Today is still Thursday.`

### 4.3.3 Breaking Out of a Loop

Sometimes you finish the task at hand before you are done with the loop. If this is a possibility for you then you may arrange to break out of the loop with the `break`

reserved-word.

Suppose for example that you want a function that searches through a vector for a given element, updating the user along the way as to the progress of the search. You can try something like this:

```
# function to find index of element in vector.
# returns -1 if elem is not in vector
verboseSearch <- function(elem, vec) {
# The following logical keeps track of whether
# we have found the element.
# We have not yet begun the search so start it
# at FALSE.
found <- FALSE
# validate input:
if ( length(vec) == 0 ) {
cat("The vector empty. No way does it contain ",
elem, ".", sep = "")
return(-1)
}
# check the elements of vector:
for ( i in 1: length(vec) ) {
if ( vec[i] == elem ) {
# record that we found the element:
found <- TRUE
break
} else {
# report no match at this index:
cat("Checked index ", i,
" in the vector. No match there ...\n", sep = "")
}
}
if ( found ) {
# report success:
cat("Found ", elem, " at index ", i, ".\n", sep = "")
return(i)
} else {
# report failure:
cat(elem, " is not in the vector.\n", sep = "")
return(-1)
}
}
```

Let’s see our function in action:

```
people <- c("Dorothy", "Tin Man", "Scarecrow", "Lion")
scarecrowPlace <- verboseSearch("Scarecrow", people)
```

```
## Checked index 1 in the vector. No match there ...
## Checked index 2 in the vector. No match there ...
## Found Scarecrow at index 3.
```

In the code for `verboseSearch()`

you will notice that there is an initial check on the length of the vector. This is actually important. If a user were to enter an empty vector, then its length would be 0. Then in the loop the sequence would be `1:0`

, which is the vector with elements 1 and 0. But look at what happens when you ask for any element of a zero-length vector:

`## [1] NA`

Then check out what happens if you compare an `NA`

to a string:

`## [1] NA`

Now look at what happens in an `if`

statement where the condition is `NA`

:

`## Error in if (NA) { : missing value where TRUE/FALSE needed`

Checking that the input vector has positive length is an another example of *validating* input. Remember: when you write functions for other people to use it can be helpful to have the function validate its input instead of allowing R to throw obscure error messages at the user.

### 4.3.4 Solving the Empty-Vector Problem in `for`

-Loops with `seq_along()`

In the previous section we considered a possible problem with `for`

-loops of the following form:

In the above loop `vec`

could be thought of as a “loop-defining” vector: its length determines the sequence of values 1, 2, 3 … for the index `i`

. This sequence of values is supposed to end at the length of `vec`

.

The problem is that if `vec`

happens to be an empty vector then we probably don’t want to enter the loop at all. However, the length of an empty vector is 0, and so the vector `1:length(vec)`

actually works out to be a vector with two elements:

Hence R will go through the loop twice: once when `i`

is 1, and again when `i`

is 0. Depending on what the loop does, very unexpected results could be produced.

In the previous section we dealt with the problem by writing an `if`

-statement that provides the proper response when `vec`

is empty. In many circumstances however, all we need to do is to make sure that the loop is skipped when the vector is empty.

A handy way to ensure skipping is to use the function `seq_along()`

. Given any non-empty vector, `seq_along()`

produces a sequence-vector that begins with 1 and ends with the length of the vector, thus:

`## [1] 1 2 3`

On the other hand, if the vector is empty, then `seq_long()`

returns an empty numeric vector:

`## integer(0)`

Now consider the loop inside the following function:

```
loopy <- function(vec) {
for ( i in seq_along(vec) ) {
cat("This time i is ", i, ".\n", sep = "")
}
}
```

Given a non-empty vector, it goes through the loop a number of times equal to the length of the vector:

```
## This time i is 1.
## This time i is 2.
## This time i is 3.
```

On the other hand, when given an empty vector the function does not enter the loop at all:

When you are writing a program that is complex enough that you don’t know whether the loop-defining vector might be empty, it is good practice to use `seq_along()`

as a safeguard.

### 4.3.5 Skipping Ahead in a Loop

Depending on what happens within a loop, you might sometimes wish to skip the remaining code within the loop and proceed to the next iteration. R provides the reserved-word `next`

for this purpose. Here is a simple example:

```
vec <- c("a","e", "e", "i", "o", "u", "e", "z")
# shout ahoy when you see the specified element
verboseAhoy <- function(elem, vec) {
if (length(vec) > 0) {
for ( i in 1: length(vec) ) {
if ( vec[i] != elem) next
cat("Ahoy! ", elem, " at index ", i, "!\n", sep = "")
}
}
}
verboseAhoy("e", vec)
```

```
## Ahoy! e at index 2!
## Ahoy! e at index 3!
## Ahoy! e at index 7!
```

When the `vec[i] !== elem`

condition is true, R immediately skips the rest of the loop, increments the value of the index variable `i`

, and runs through the loop again.

You can always accomplish the skipping without using `next`

explicitly, but it’s nice to have on hand.

### 4.3.6 Repeat

For-loops are pretty wonderful, but they are best used in circumstances when you know how many times you will need to loop. When you need to repeat a block of code until a certain condition occurs, then the `repeat`

reserved-word might be a good choice.

For example, suppose you want to play the number-guessing game with the user, but let her keep guessing until either she gives up or gets the correct answer. Here’s an implementation using `repeat`

:

```
n <- 20
number <- sample(1:n, size = 1)
cat("I'm thinking of a whole number from 1 to ", n, ".\n", sep = "")
repeat {
guess <- readline("What's your guess? (Enter q to quit.) ")
if ( guess == "q" ) {
cat("Bye!\n")
break
} else if ( as.numeric(guess) == number ) {
cat("You are correct! Thanks for playing!")
break
}
# If we get here, the guess was not correct:
# loop will repeat!
}
```

The game works well enough, but if you give it a try, you are sure to find it a bit fatiguing. It wold be nice to give the user a hint after an incorrect guess. Let’s revise the game to tell the reader whether her guess was high or low. While we are at it, let’s cast the game into the form of a function.

```
numberGuess <- function(n) {
number <- sample(1:n, size = 1)
cat("I'm thinking of a whole number from 1 to ", n, ".\n", sep = "")
repeat {
guess <- readline("What's your guess? (Enter q to quit.) ")
if (guess == "q") {
cat("Bye!\n")
break
} else if (as.numeric(guess) == number) {
cat("You are correct! Thanks for playing!")
break
}
# If we get to this point the guess was not correct.
# Issue hint:
hint <- ifelse(as.numeric(guess) > number, "high", "low")
cat("Your guess was ", hint, ". Keep at it!\n", sep = "")
# Repeat loop
}
}
```

A typical game:

```
> numberGuess(100)
I'm thinking of a whole number from 1 to 100.
What's your guess? (Enter q to quit.) 50
Your guess was high. Keep at it!
What's your guess? (Enter q to quit.) 25
Your guess was high. Keep at it!
What's your guess? (Enter q to quit.) 12
Your guess was low. Keep at it!
What's your guess? (Enter q to quit.) 18
Your guess was low. Keep at it!
What's your guess? (Enter q to quit.) 22
Your guess was high. Keep at it!
What's your guess? (Enter q to quit.) 20
You are correct! Thanks for playing!
```

### 4.3.7 While

The reserved word `while`

constructs a loop that runs as long as a specified condition is true. Unlike `repeat`

, which launches directly into the loop, the condition for `while`

is evaluated prior to the body of the loop. If the condition is false at the beginning, the code in the body of the loop is never executed.

`verboseSearch()`

could be re-written with `while`

:

```
verboseSearch <- function(elem, vec) {
found <- FALSE
if ( length(vec) == 0 ) {
cat("The vector is empty. No way does it contain ",
elem, ".\n", sep = "")
return(-1)
}
# index of vec (start looking at 1):
i <- 1
while ( !found & i <= length(vec) ) {
if ( vec[i] == elem ) {
found <- TRUE
break
}
cat("No match at position ", i, " ...\n")
i <- i + 1
}
if ( found ) {
# report success:
cat("Found ", elem, " at index ", i, ".\n", sep = "")
return(i)
} else {
# report failure:
cat(elem, " is not in the vector.\n", sep = "")
return(-1)
}
}
```

### 4.3.8 Practice Problems

You want to compute the square roots of the whole numbers from 1 to 10. You could do this quite easily as follows:

`## [1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 ## [8] 2.828427 3.000000 3.162278`

However, you want to practice using

`for`

-loops. Write a small program that uses a loop to compute the square roots of the whole numbers from 1 to 10.Think about the practice problem

`findKarl()`

from the previous section. Write a new version that uses a loop.You want to append

`"the Dog"`

to the end of each element of a character vector of dog-names. Sadly, you have not yet realized that the`paste()`

function is very much up to the task:`## [1] "Bo the Dog" "Gracie the Dog" "Rover the Dog" "Anya the Dog"`

You decide to use a

`for`

-loop. Write the code.Write a function called

`triangleOfStars()`

that produces patterns liks this:`**** *** ** *`

It should take a single parameter

`n`

, the number of asterisks in the first “line” of the triangle. Use a`for`

-loop in the body of the function.- Write a function called
`trianglePattern()`

that produces patterns like the ones from the previous problem, but using any character that the user specifies. The function should take two parameters:`char`

, a string giving the character to use. The default value should be`"*"`

.`n`

, the number of asterisks in the first “line” of the output.

Typical examples of use should be as follows:

`## xxxxx ## xxxx ## xxx ## xx ## x`

`## ****** ## ***** ## **** ## *** ## ** ## *`

Rewrite

`triangleOfStars()`

so that it uses a`while`

-loop in its body.Rewrite

`triangleOfStars()`

so that it still uses a`while`

-loop in its body, but produces output like this:`* ** *** ****`

Write a function called

`cheerleader()`

that badgers you for each of the letters in a word until you give it all of the letters, one by one. It should work like this:`> cheerleader(cheer = schoolMascot) Gimme a T! T Gimme a I! i Gimme a I! I Gimme a G! gee Gimme a G! I said gee!! Gimme a G! G Gimme a E! E Gimme a R! Are you going to do this all day? Gimme a R! R Gimme a S! Stop it, please! Gimme a S! S >`

Write a function called

`ageReporter()`

that reports the ages of given individuals. It should take two parameters:`people`

: a character vector of names of the people of interest.`ages`

: a vector containing the ages of the people of interest.

The function should perform some validation: it should stop the user with a helpful message if the number of people supplied and the number of ages supplied are not the same, or if one of the supplied vectors has length zero.

Typical examples of use would be as follows:

`## Gina is 25 year(s) old. ## Hector is 22 year(s) old.`

`## Number of people must equal number of ages!`

`## Provide at least one age!`

### 4.3.9 Solutions to the Practice Exercises

Here’s a suitable program:

`## [1] 1.000000 1.414214 1.732051 2.000000 2.236068 2.449490 2.645751 ## [8] 2.828427 3.000000 3.162278`

You could do this:

Here you go:

`doggies <- c("Bo", "Gracie", "Rover", "Anya") titledDoggies <- character(length(doggies)) for (i in 1:length(doggies) ) { titledDoggies[i] <- paste(doggies[i], "the Dog") } titledDoggies`

`## [1] "Bo the Dog" "Gracie the Dog" "Rover the Dog" "Anya the Dog"`

Here’s the code:

`triangleOfStars <- function(n) { for ( i in n:1 ) { line <- rep("*", times = i) cat(line, "\n", sep = "") } }`

Try it out:

`## ***** ## **** ## *** ## ** ## *`

Here’s the code:

Here’s the code:

`triangleOfStars <- function(n) { while ( n >= 1 ) { line <- rep("*", times = n) cat(line, "\n", sep = "") n <- n - 1 } }`

Try it out:

`## ***** ## **** ## *** ## ** ## *`

Here’s the code:

`triangleOfStars <- function(n) { m <- 1 while ( m <= n ) { line <- rep("*", times = m) cat(line, "\n", sep = "") m <- m + 1 } }`

Try it out:

`## * ## ** ## *** ## **** ## *****`

Here is the requested annoying function, using a

`repeat`

-loop with`break`

inside a`for`

-loop:Here is one possibility:

`ageReporter <- function(people, ages) { ## validation: p <- length(people) a <- length(ages) if (p == 0) { return(cat("Provide at least one person!\n")) } if (a == 0) { return(cat("Provide at least one age!\n")) } if (!(p == a & p > 0)) { return(cat("Number of people must equal number of ages!\n")) } ## process: for (i in 1:length(people)) { cat(people[i], " is ", ages[i], " year(s) old.\n", sep = "") } }`