countdown <- function (){
for (num in 10:1){ # each time through the loop another value from 10:1 is assigned to num
cat(num," ") # display num followed by a space
Sys.sleep(1.5) # sleep for 1.5 seconds
}
cat("blastoff!") # after the loop has finished, display "blastoff!"
}41 41. for loops
A “for loop” is similar to a “while loop” in that it causes a body of code to be repeated more than once. The for loop is DIFFERENT from the while loop in HOW it accomplishes the repetition.
If you need code to be repeated you can ALWAYS use a WHILE loop. However, sometimes the while loop can be a little confusing. A “for loop” is an alternative to a “while loop” that is usually easier to understand than the equivalent code that uses a while loop (we’ll explain why later).
Unfortunately, you cannot always use a FOR loop. A for loop can only be used if it’s possible to know before the loop starts exactly how many times the loop will need to iterate (i.e. “go around”). The reason for this will be explained later.
It’s probably easiest to understand how the for loop works if you examine the example code that appears below. However, the following is an attempt to explain in words how the for loop works. I recommend that you look at the examples below and then come back and read this more detailed explanation.
Code of “for loops” is arranged in the following format.
for ( VARIABLE in VECTOR_OR_LIST ){
# This area between the curly braces is the "body" of the loop.
# The code in the "body" is repeated once for each value in the VECTOR_OR_LIST.
# Each time this code is executed the VARIABLE is automatically
# set to the next value in the VECTOR_OR_LIST
# Code in the body may refer to the VARIABLE but there is no
# requierment that they do.
}NOTES:
VARIABLE is the name of a variable. This variable is created just for the purpose of the for loop. You cannot refer to this variable outside of the code for the for loop. (If the same name is used for a variable outside of the for loop it is a different variable).
VECTOR_OR_LIST is a vector or a list. This could be a variable name that was created before the for loop started or it could be the result a function that creates a vector or a list.
The code executes in the following way:
step 1. The first value in the vector (or list) is assigned to the variable.
step 2. The body of the loop (i.e. code between {curly braces}) is executed. [The code in the body may refer to the variable but it doesn’t have to.]
step 3. After the {body} finishes executing once, the next value from the vector (or list) is assigned to the variable.
step 4. The body of the for loop is then executed again. However, this time the value of the variable is the 2nd value from the vector (or list).
step 5. The for loop keeps replacing the value of the variable with the next value from the vector (or list) and then doing the code in the {body} until it runs out of values from the vector (or list).
step 6. After all values from the vector (or list) have been processed, the loop is finished and execution continues with the line after the end of the body of the loop - i.e. the line after the “}”
41.1 Example - countdown
Everything you need to know about a for loop is in the first line of the loop (i.e. the line that starts with the word “for”). In the following example, the first line says: for(num in 10:1). See below for more explanation.
countdown()10 9 8 7 6 5 4 3 2 1 blastoff!
In this example, num is a variable and 10:1 is a vector, i.e. c(10,9,8,7,6,5,4,3,2,1). The for loop automatically assigns a value from the vector to the variable. Then it does the body. Then it assigns the next value from the vector to the variable and does the body again. It keeps doing this until all of the values have been processed by the body of the loop. Specifically, in this example:
The 1st value from the vector (i.e. 10) is assigned to the variable, num Then the body of the loop is executed.
The 2nd value from the vector (i.e. 9) is assigned to the variable, num Then the body of the loop is executed.
The 3rd value from the vector (i.e. 8) is assigned to the variable, num Then the body of the loop is executed.
etc …
The 10th value from the vector (i.e. 1) is assigned to the variable, num Then the body of the loop is executed.
At this point, the loop is finished and the function continues with the code after the body of the loop.
41.2 You can rewrite any for loop as a while loop
A for loop can ALWAYS be rewritten as a while loop (but not necessarily not vice versa - see more discussion below). The following code rewrites the above example to use a while loop.
countdownWithWhile <- function (){
num = 10 # setup the variables to be used in the condition of the while
while(num >= 1){ # condition that's TRUE when loop should run and FALSE when loop should end
cat(num, " ")
Sys.sleep(0.25)
num <- num - 1 # change some variable that is part of the condition
} # END OF WHILE - code below will only happen after while finishes
cat("blastoff!")
}
countdownWithWhile()10 9 8 7 6 5 4 3 2 1 blastoff!
A while loop version is usually a little harder to understand than an equivalent for loop.
To understand how many times a for loop will iterate (i.e. go around) you just have to look at the first line of the for loop.
By contrast, to understand how a while loop works, you also have to look at
- the first line of the loop
- the code before the while loop that sets up the variables that the loop needs and
- you also have to track how the variables used in the condition of the while loop are changed by the code in the body of the while loop.
A for loop is usually easier to understand because, all of this info is found in the first line of the for loop.
41.3 A “for loop” can use ANY vector or list
A “for loop” can use ANY vector or list
Other examples of vectors - character and logical vectors - numeric vectors that don’t count by ones
countByTwos_for = function(){
vec = seq(2,10, by=2)
for ( num in vec){
cat("I like number", num, "\n")
}
}countByTwos_for()I like number 2
I like number 4
I like number 6
I like number 8
I like number 10
The following is the same example, rewritten to use a while loop.
# same example with a while loop
countByTwos_while = function(){
num = 2
while ( num <= 10){
cat("I like number", num, "\n")
num = num + 2
}
}countByTwos_while()I like number 2
I like number 4
I like number 6
I like number 8
I like number 10
41.3.1 Example: A for loop with a character vector
The following uses a character vector in the for loop:
#.................
# With a for loop
#.................
anotherExample_for = function( foods ){
cat("Hello. \n")
for (item in foods) {
cat(item, "is yummy.\n")
cat("Everyone likes", item, ".\n")
cat("I hope we have", item, "for supper!\n\n")
}
cat("bye bye.\n")
}
anotherExample_for (c("pizza", "french fries", "burger", "chicken", "ice cream"))Hello.
pizza is yummy.
Everyone likes pizza .
I hope we have pizza for supper!
french fries is yummy.
Everyone likes french fries .
I hope we have french fries for supper!
burger is yummy.
Everyone likes burger .
I hope we have burger for supper!
chicken is yummy.
Everyone likes chicken .
I hope we have chicken for supper!
ice cream is yummy.
Everyone likes ice cream .
I hope we have ice cream for supper!
bye bye.
anotherExample_for (c("cake", "lasanga", "chullent"))Hello.
cake is yummy.
Everyone likes cake .
I hope we have cake for supper!
lasanga is yummy.
Everyone likes lasanga .
I hope we have lasanga for supper!
chullent is yummy.
Everyone likes chullent .
I hope we have chullent for supper!
bye bye.
anotherExample_for (character(0))Hello.
bye bye.
anotherExample_for( list("apple", "orange")) # for loops also work with listsHello.
apple is yummy.
Everyone likes apple .
I hope we have apple for supper!
orange is yummy.
Everyone likes orange .
I hope we have orange for supper!
bye bye.
41.3.2 same example with a while loop
As we said above we can always rewrite a for loop as a while loop (but necessarily not vice versa - see more discussion below). The following shows the same example but this time with a while loop.
The following code uses a while loop to process each position in the vector (or the list). This is what the for loop actually does for you, but we can do it ourselves.
The following shows a general way to convert ANY for loop into a while loop. We highlighted the lines of code in this version that are different from the previous version. All other lines of code are EXACTLY the same.
anotherExample_while = function( foods ){
cat("Hello. \n")
position = 1 # new line
while(position<=length(foods)){ # new line
item = foods[[position]] # new line; [[ ]] works with both lists and vectors
cat(item, "is yummy.\n")
cat("Everyone likes", item, ".\n")
cat("I hope we have", item, "for supper!\n\n")
position = position + 1 # new line
}
cat("bye bye.\n")
}
# This produces the exact same results as the for loop
anotherExample_while (c("pizza", "french fries", "burger", "chicken", "ice cream"))Hello.
pizza is yummy.
Everyone likes pizza .
I hope we have pizza for supper!
french fries is yummy.
Everyone likes french fries .
I hope we have french fries for supper!
burger is yummy.
Everyone likes burger .
I hope we have burger for supper!
chicken is yummy.
Everyone likes chicken .
I hope we have chicken for supper!
ice cream is yummy.
Everyone likes ice cream .
I hope we have ice cream for supper!
bye bye.
anotherExample_while (c("cake", "lasanga", "chullent"))Hello.
cake is yummy.
Everyone likes cake .
I hope we have cake for supper!
lasanga is yummy.
Everyone likes lasanga .
I hope we have lasanga for supper!
chullent is yummy.
Everyone likes chullent .
I hope we have chullent for supper!
bye bye.
anotherExample_while (character(0))Hello.
bye bye.
anotherExample_while( list("apple", "orange")) # careful Hello.
apple is yummy.
Everyone likes apple .
I hope we have apple for supper!
orange is yummy.
Everyone likes orange .
I hope we have orange for supper!
bye bye.
41.4 Recipe to convert any for loop into a while loop
Any code with a “for loop” can be converted to equivalent code with a while loop by following these steps:
step 1: before the while loop copy the vector (or list) from the for loop in to a new variable, eg. vecOrList
step 2: create a variable for the position in the vector (or list) to be processed at each iteration through the body of the loop
step 3: write the while loop with the condition:
while ( position <= length(vecOrList) )step 4: Write a line at the end of the body of code for the while loop that increments (i.e. adds 1 to) the position variable
We demonstrate this by following these steps to rewrite the code from the countByTwos_for function to an equivalent function with a while loop.
Rewrite the for countByTwos function to use a while loop in a way that we can apply the same approach to convert ANY for loop into an equivalent while loop. Note that this version is not the same as the previous version that used a while loop. Both versions work, but the previous version is probably easier to understand. The approach taken with this version can be applied to any for loop.
countByTwos_while_version2 = function(){
# step 1: copy the vector from the for loop in to a variable
vec = seq(2,10, by=2)
# step 2: create a variable for the position in the vector (or list)
# to be processed at each iteration through the body of the loop
position = 1
while ( position <= length(vec)){ # step 3: write the condition for the while
cat("I like number", vec[position], "\n")
# step 4: at the end of the body of the loop add one to the position
# variable
position = position + 1
}
}countByTwos_while_version2()I like number 2
I like number 4
I like number 6
I like number 8
I like number 10
41.5 — Practice —
##################################################################.
# QUESTION
##################################################################.
#
# Write a function that takes a matrix, m, as an argument.
# The function should return a new matrix that
#
# multiplies the 1st row by 10
# multiplies the 2nd row by 100
# multiplies the 3rd row by 1000
# etc ... for all rows of the matrix
#
# (a) - Write the function using a for loop
# (b) - Write the function using a while loop
#
##################################################################.##################################################################.
# QUESTION
##################################################################.
#
# Write a function that takes a matrix, m, as an argument.
# The function should return a new matrix that
#
# adds 2 (i.e. 1+1) to the value in position 1,1
# adds 3 (i.e. 1+2) to the value in position 1,2
# adds 4 (i.e. 1+3) to the value in position 1,3
# ... and similarly for the rest of the values in row 1
#
# adds 3 (i.e. 2+1) to the value in position 2,1
# adds 4 (i.e. 2+2) to the value in position 2,2
# adds 5 (i.e.2+3) to the value in position 2,3
# ... and similarly for the rest of the values in row 2
#
# etc ... for all rows in the matrix
#
# Use nested loops for your answer
#
# (a) - Write the function using nested for loops
# (b) - Write the function using nested while loops
# (c) - Write the function using nested loops, one should be a for loop
# and one a while loop (your choice which is which)
#
##################################################################.41.6 You CANNOT use a for loop for some problems
41.6.1 guessingGame - can’t use for loop
#-----------------------------------------------------------------
# SOMETIMES you MUST use a while loop.
#-----------------------------------------------------------------
# Some problems that cannot be coded with a for loop
# but rather require that you use a WHILE loop.
#
# To use a for loop, you must be able to construct a vector that
# contains all possible values that you will loop through.
#
# However, sometimes, you don't know what those values are or how
# many times you will need to process the loop.
#-----------------------------------------------------------------
# EXAMPLE - guessing game cannot be done with a for loop.
# The loop can go on forever if the user keeps getting the wrong answer.
# The following function is coded with a WHILE loop.
# (you cannot write this function with a for loop)
guessingGame <- function(low=1, high=10){
num <- sample(low:high, 1)
numGuesses <- 1
guess <- as.numeric( readline( paste0(
"guess a number between ", low, " and ", high, ": " )))
while(guess != num) {
if (guess < num){
guess <- as.numeric( readline("higher, guess again: ") )
} else if (guess > num) {
guess <- as.numeric( readline("lower, guess again: ") )
}
numGuesses <- numGuesses + 1
}
cat("You got it in", numGuesses, "guesses.")
}
#guessingGame() # run this in RStudio41.6.2 firstNPrimes - can’t use for loop
# EXAMPLE - firstNPrimes cannot be done with a for loop. I have
# no idea how many numbers I'll have to check to find the 1000th prime
# or the millionth prime. There is no simple formula that will give
# that answer. Therefore, there is no way to know how long the vector
# should be for the for loop.41.7 With a for loop you must use a variable even when there is no need for the value.
With a for loop you must use a variable even when there is no need for the value.
The following for loop does NOT make reference to the num variable at all. However, it is still required to be specified in the code.
sayHello = function( numTimes ) {
for (num in 1:numTimes){
name = readline("What is your name? ")
cat("Hello ", name, ".\n", sep="")
cat("I'm fine.\n")
cat("How are you doing?\n\n")
}
cat("bye bye.")
}
sayHello(3) # say hello three timesWhat is your name? Alice
Hello Alice.
I'm fine.
How are you doing?
What is your name? Bob
Hello Bob.
I'm fine.
How are you doing?
What is your name? Carol
Hello Carol.
I'm fine.
How are you doing?
bye bye.
41.8 NEVER change the value of the for loop variable/vector in the body
DO NOT CHANGE THE VALUE OF THE FOR LOOP VARIABLE IN THE BODY!!!
DO NOT CHANGE THE VALUE OF THE FOR LOOP VECTOR IN THE BODY!!!
The code in the body should NEVER change the value of the for loop variable directly.
All changes to the value of the for loop variable should only be done automatically by the for loop mechanism, i.e. the next value from the vector is AUTOMATICALLY assigned to the for loop variable each time through the loop.
Explicitly changing the value of the for loop variable in the body of the loop is very confusing and is considered VERY VERY sloppy coding by programmers everywhere.
Similarly, the code in the body should NEVER change the value of the for loop vector. The vector should have a value when the for loop starts and the value of the vector should NEVER change as the for loop is executing.
Changing the value of the vector of the for loop in the body of the loop is very confusing and is considered VERY VERY sloppy coding by programmers everywhere.
41.9 DIFFERENCE BETEWEN FOR LOOPS AND WHILE LOOPS
A for loop is a convenience but NOT a necessity.
It IS TRUE that any code written with a for loop CAN be converted to use a while loop instead.
HOWEVER, it is NOT TRUE that any code written with a while loop can be converted to use a for loop.
A for loop can only be used when you can anticipate how many times the code will loop before the loop starts.
For example, the guessing game program that we wrote in an earlier class cannot be written with a for loop since it is impossible to know in advance how many times the loop will need to “go around”.
isPrime CAN be written with a for loop since you know that you even before the for loop needs to “go around” at most sqrt(n) times.
firstNprimes cannot be written with a for loop since we have no idea in advance how large the nth prime will be, e.g. how large is the one millionth prime number??? Therefore we cannot anticipate before the loop starts how many times the code needs to be repeated.
41.10 Practice - Some “toy” funcitons to “draw” with character values.
The following functions are classic exercises for students who are trying to really understand how “nested loops” wprk. The functions don’t really do anything so useful. However thinking through how these functions work will sharpen your thought process about nested loops.
Going through these exercises in similar to how someone learning to play a musical instrument will practice “playing scales”. No one is really interested in playing scales but the exercise sharpens the mind.
41.10.1 drawing a box with numbers
#-------------------------------------------------------------------------
# QUESTION: Write a function: box = function(rows, cols)
# to draw a box in the following pattern using nested for loops
#
# > box (rows = 3 , cols = 4)
# 3333
# 2222
# 1111
#-------------------------------------------------------------------------#-------------------------------------------------------------------------
# QUESTION - rewrite the box function from the previous question
# to use nested while loops
#-------------------------------------------------------------------------41.10.2 drawing a triangle with numbers
#-------------------------------------------------------------------------
# QUESTION - Write a function
#
# triangle = function(size)
#
# that draws a triangle in the following pattern using nested for loops.
#
# > triangle(3)
# 1
# 21
# 321
#-------------------------------------------------------------------------#-------------------------------------------------------------------------
# QUESTION - rewrite the triangle function from the previous question
# to use nested while loops
# > triangle(3)
# 1
# 21
# 321
#-------------------------------------------------------------------------41.10.3 triangle with a different pattern
#-------------------------------------------------------------------------
# QUESTION - Write a function
#
# triangle = function(size)
#
# that draws a triangle in the following pattern using nested for loops.
#
# > triangle(3)
# 1
# 21
# 321
#
# > triangle(4)
# 1
# 21
# 321
# 4321
#
# HINT: you can think of this "triangle" as a "box" but where some of the
# spots are actually spaces. It might help to view the spaces as periods
# so that you can see them. Think about how to draw each of the rows -
# you can split up each row into (a) drawing the periods and (b) drawing the
# numbers
#
# > triangle(3)
# ..1
# .21
# 321
#
# > triangle(4)
# ...1
# ..21
# .321
# 4321
#-------------------------------------------------------------------------41.10.5 QUESTION: myUnlist(SOME_LIST)
#------------------------------------------------------------
# QUESTION
#
# The unlist function can be used on a dataframe (since a dataframe is a list).
# (see example below)
#
# Write a function myUnlist that does the same thing as the unlist
# function. However, your function does NOT need to create names for
# the answer vector.
#
# ARGUMENTS: lst is a list (remember a dataframe is also a list)
#
# The function should return a character vector that combines all
# the values from the list (or all the columns from the dataframe)
# into a single vector.
#------------------------------------------------------------#------------------------------------------------------------
# QUESTION
#
# Rewrite myUnlist to use a for loop
#------------------------------------------------------------41.10.6 QUESTION: dfToVec - only character columns
#------------------------------------------------------------
# Write a function, dfToVec, that converts the contents of a
# dataframe into a vector (same as the myUnlist function above.
# However, modify the code so that the vector
# that is returned ONLY INCLUDES the values that
# are in character columns of the dataframe.
#------------------------------------------------------------41.10.7 QUESTION: dfToVec - only positive numbers, TRUE values and words that start with “a”
#------------------------------------------------------------
# QUESTION
#
# Write a function that takes a dataframe, df
# and returns a character vector.
#
# The vector should contain
# - all positive numbers from numeric columns
# - all TRUE values from logical columns
# - all character values that start with an "a" from character columns
#------------------------------------------------------------41.11 strsplit function
We will use this function below in some loop examples. Therefore let’s cover the basics of how this function works it here first before using it in a more involved loop example below.
#...............
# strsplit
#...............
# strsplit will split up each value in a
# character vector into multiple values.
#
# IMPORTANT: The return value of strsplit is a LIST.
# View the help page by typing:
#
# ?strsplit
# multiple values in the original vector
people = c("Cohen,Sam","Jones,Bob","Andrews,Claire")
people[1] "Cohen,Sam" "Jones,Bob" "Andrews,Claire"
length(people) [1] 3
splitPeople <-strsplit(people,",")
splitPeople[[1]]
[1] "Cohen" "Sam"
[[2]]
[1] "Jones" "Bob"
[[3]]
[1] "Andrews" "Claire"
# print the first name (i.e. the 2nd position) for "Andrews,Claire"
splitPeople[[3]][2][1] "Claire"
# use a colon as a separator
places <- c("New York:NY:USA", "Jerusalem::IL", "LA:CA:USA", "Miami:FL:USA")
places[1] "New York:NY:USA" "Jerusalem::IL" "LA:CA:USA" "Miami:FL:USA"
strsplit(places, ":")[[1]]
[1] "New York" "NY" "USA"
[[2]]
[1] "Jerusalem" "" "IL"
[[3]]
[1] "LA" "CA" "USA"
[[4]]
[1] "Miami" "FL" "USA"
# Note that strsplit returns a LIST even if there is only one
# value in the character vector
fruit = c("apple,orange,pear")
fruit[1] "apple,orange,pear"
length(fruit)[1] 1
splitFruit <- strsplit(fruit, ",")
splitFruit[[1]]
[1] "apple" "orange" "pear"
# display just the 2nd fruit (notice the [[double-brackets]][single-brackets])
splitFruit[[1]][2][1] "orange"
# If the separator is the "empty string" (i.e. "") then
# every character (e.g. letter, digit, space,!,@,#,$,%, etc)
# is split into separate character values
words = c("racecar", "pineapple", "cart")
strsplit(words,"")[[1]]
[1] "r" "a" "c" "e" "c" "a" "r"
[[2]]
[1] "p" "i" "n" "e" "a" "p" "p" "l" "e"
[[3]]
[1] "c" "a" "r" "t"
# Remember that strsplit returns a list EVEN if there
# is only one character value being split. You will need to
# keep this in mind when writing the code.
word <- "racecar"
y <- strsplit(word,"")
y[[1]]
[1] "r" "a" "c" "e" "c" "a" "r"
y[1] # still a list [[1]]
[1] "r" "a" "c" "e" "c" "a" "r"
y[[1]] # the vector without the surrounding list[1] "r" "a" "c" "e" "c" "a" "r"
unlist(y) # the vector without the surrounding list[1] "r" "a" "c" "e" "c" "a" "r"
# display the 2nd letter of the word
y[[1]][2] # one way[1] "a"
unlist(y)[2] # another way[1] "a"
z <- y[[1]] # you could use a variable
z[2][1] "a"
41.12 QUESTION: isPalindrome(x)
#--------------------------------------------------------------
# A plaindrome is a word that reads the same forwards and
# backwards.
# Example: racecar is a palindrome
# pineapple is not
# abcdxba is NOT a palindrome
# abcdcba is a palindrome
#
# Write a function isPalindrome(x)
#
# ARGUMENTS
# - x is expected to be a character vector with one item in it
#
# isPalindrome(x) should return TRUE if x is a palindrome
# and return FALSE if it isn't
#
# PART A - code this using a while loop
#
# PART B - code this using a for loop
#--------------------------------------------------------------41.12.1 isPalindrome(x) with a while loop
41.13 QUESTION: checkPalindromes()
Write a function, checkPalindromes = function() that asks the user to enter a word. The function should then call the isPalindrome function with the word the user entered and report on whether the word is a palindrome or not. The user should be asked to enter additional words until the user types the word “end” to end the program.