library(learnr)
tutorial_options(exercise.reveal_solution = FALSE)
gradethis::gradethis_setup()

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = ">",
  error = TRUE
)
set.seed(123)

Loops

Loops allow you to repeat the same thing again and again. If you are doing something more than two or three times, you should instead use a loop to avoid having to copy and paste the same code over and over again. Since duplicating code risks not only mistyping or not copying correctly, but also creating confusion by filling your R scripts with nearly identical chunks of code. There are fundamentally two kinds of loop:

for loops

for loops allow us to repeat something for a fixed number of times (e.g. once per element of a vector, once per row of a data frame, etc.).

A for loop always has the same style:

for (one in many) {
  # Do something

  # ... exciting code maybe using the variable
  #     one (but it doesn't have to) ...
}

It looks a bit like a function, but what it does is set the variable one to each of the values in many one at a time, and then does something. Here are two very simple examples to get an idea of what it might do:

greetings <- c("Hello", "Goodbye")
for (word in greetings) {
  print(word)
}

Try changing the greetings variable. You'll see that the loop runs once for each word in the greetings vector. We can use this to do something a fixed number of times without necessarily caring about the entries:

# Make a sequence of 7 numbers from 1 to 7
times <- seq(from = 1, to = 7) # This is the same as 1:7 or seq(1, 7)

# Print "I'm in a loop" repeatedly
for (iteration in times) {
  print("I'm in a loop")
}

Change the length of the times vector. You'll see that it prints out the text once for each element in times even though it never looks to see what iteration is (it doesn't care).

Now look at this code:

# A function to add one to a number
add_one <- function(number)
  number + 1

# Initialise the value
current.value <- 0

current.value <- add_one(current.value)
current.value <- add_one(current.value)
current.value <- add_one(current.value)
current.value <- add_one(current.value)
current.value <- add_one(current.value)

# Output the updated current.value at the end to see what happened
current.value
grade_result(pass_if(~identical(.result, 10)))

It just runs our add_one() function from earlier five times to update the variable current.value to 5. Change the code so it does this ten times to produce 10.

Now look at this next piece of code:

# A function to add one to a number
add_one <- function(number) {
  number + 1
}

# Initialise the value
current.value <- 0

# This now is a loop!
for (loop in 1:10) {
  current.value <- add_one(current.value)
}

# Output the updated current.value at the end to see what happened
current.value
grade_result(pass_if(~identical(.result, 100)))

It does the same thing - it calls add_one() ten times, but it's much shorter, which generally makes it easier to see what's going on. Now change the code to call add_one() 100 times. Hopefully you can see that this is really starting to help now!

And you can probably imagine that if it was any more complicated that just copying and pasting one line it would make it much easier to see that it was doing exactly the same thing each time too.

Finally, add in the single line print(paste("Loop number", loop)) into the loop at the end (between lines 11 and 12) and run it. You'll now be able see each step of the loop running. We'll use techniques like this later to check that the loop is doing the right thing.

Note also that unlike functions, variables changed in for loops do change what happens outside the loop, so current.value is 100 at the end, and not still 0.

There is a section in R4DS (within a more general chapter on Iteration) that covers for loops. For loops are also covered by R Coder here.

while loops

while loops allow us to repeat something while a condition is still TRUE. They are particularly useful when you don't know in advance exactly how many times something will be done, but can be used in any situation. Unlike for loops though, you need to update everything manually each time around:

x <- 1
test <- x < 3 
while (test) {
  # Do something
  print(x)
  # Update x and update test
  x <- x + 1
  test <- x < 3 
}

test is checked at the beginning of each iteration, and as long as test continues to be TRUE, the code block inside the curly brackets { ... } is run. The test after the while statement acts exactly like a if statement that we looked at in A-1, except that it is run repeatedly until it is FALSE, so refer back to there to remind you how if and tests work.

Be careful though... if you don't update test inside the curly brackets, your loop will continue forever! However, if (when!) test is finally FALSE, the loop will end and R will continue on to the next line of code after the loop.

There is a section in R4DS (within a more general chapter on Iteration) that covers while loops. While loops are also covered by R Coder here.

Exercises

Now try the following exercise, combining an if statement with a for loop to print greetings (not duck):

greetings <- c("Hello", "Goodbye", "duck")
for (word ___) {
  if (___) {
    print(___)
  } 
}
greetings <- c("Hello", "Goodbye", "duck")
for (word in greetings) {
  if (word != "duck") {
    print(word)
  } 
}
grade_this_code()

Solve the following problem -- a countdown from 10 to 1.

x <- 10
while (___) {
  # Do something
  print(x)
  if (x == 1) print("Blast off!")

  # Update test
  x <- ___
}
x <- 10
while (x > 0) {
  # Do something
  print(x)
  if (x == 1) print("Blast off!")

  # Update test
  x <- x - 1
}
grade_this_code()


IBAHCM/RPiR documentation built on Jan. 12, 2023, 7:41 p.m.