rm (list=ls())
################################################################
################################################################
##
## Intro to for loop.
##
## 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 ( SOME_VARIABLE in SOME_VECTOR__OR__SOME_LIST ){
##
## # statements that
##
## # may refer to
##
## # the variable
##
## }
##
## NOTES:
##
## 1. SOME_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).
##
## 2. SOME_VECTOR__OR__SOME_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.
##
## 3. 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 "}"
##
################################################################
################################################################
34 for loops
34.1 Example - countdown
# This file goes through several examples of using a for loop and a while loop.
# Be sure to see the notes at the bottom of this file for some important
# concepts and a discussion of when a for loop is an option and when
# you must use a while loop.
#--------------------------------------------------------------------------.
# Example of a for loop
#
# 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).
#
# 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.
#--------------------------------------------------------------------------.
<- function (){
countdown 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!"
}
countdown()
10 9 8 7 6 5 4 3 2 1 blastoff!
34.2 You can rewrite any for loop as a while loop
#----------------------------------------------------------------------
# A for loop can ALWAYS be rewritten as a while loop.
#
# The following code rewrites the above example to use a while loop.
#
# The while loop version is usually a little harder to understand
# than the for loop version. 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.
#
# To understand how the while loop works, you also have to look at the first
# of the loop. However, 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.
#----------------------------------------------------------------------
<- function (){
countdownWithWhile = 10 # setup the variables to be used in the condition of the while
num
while(num >= 1){ # condition that's TRUE when loop should run and FALSE when loop should end
cat(num, " ")
Sys.sleep(0.25)
<- num - 1 # change some variable that is part of the condition
num # 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!
34.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
#----------------------------------------------------------------------
#-----------------------------------------------.
# Count by two's - with a for loop
#-----------------------------------------------.
= function(){
countByTwos_for = seq(2,10, by=2)
vec 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
#-----------------------------------------------.
# rewriting the same example with a while loop
#-----------------------------------------------.
= function(){
countByTwos_while = 2
num while ( num <= 10){
cat("I like number", num, "\n")
= num + 2
num
}
}
countByTwos_while()
I like number 2
I like number 4
I like number 6
I like number 8
I like number 10
34.4 How 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.
= function(){
countByTwos_while_version2
# step 1: copy the vector from the for loop in to a variable
= seq(2,10, by=2)
vec
# 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
= 1
position
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 + 1
position
}
}
countByTwos_while_version2()
I like number 2
I like number 4
I like number 6
I like number 8
I like number 10
34.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) the value in position 1,1
# adds 3 (i.e. 1+2) the value in position 1,2
# adds 4 (i.e. 1+3) the value in position 1,3
# ... and similarly for the rest of the values in row 1
#
# adds 3 (i.e. 2+1) the value in position 2,1
# adds 4 (i.e. 2+2) the value in position 2,2
# adds 5 (i.e.2+3) 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)
#
##################################################################.
::: {.callout-note icon=false collapse=“true”} #### click here for answer - using while loop
# Answer - while
= function( m ){
changeRows_while
= 1
row while( row <= nrow(m) ){
= 1
col while(col <= ncol(m)){
= m[row,col] + row + col
m[row, col] = col+1
col
}
= row + 1
row
}
return (m)
}
= matrix( 0 , nrow=3, ncol=6)
m m
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 0 0 0 0 0 0
[2,] 0 0 0 0 0 0
[3,] 0 0 0 0 0 0
changeRows_while(m)
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 2 3 4 5 6 7
[2,] 3 4 5 6 7 8
[3,] 4 5 6 7 8 9
34.6 ANOTHER EXAMPLE: display character values in a vector
#----------------------------------------------------
# ANOTHER EXAMPLE: display character values in a vector
#----------------------------------------------------
#.................
# With a for loop
#.................
= function( foods ){
anotherExample_for
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 lists
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.
#...................
# With a while loop
#...................
# You can convert any for loop to a while loop.
# Use the 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.
= function( foods ){
anotherExample_while
cat("Hello. \n")
= 1 # new line
position while(position<=length(foods)){ # new line
= foods[[position]] # new line; [[ ]] works with both lists and vectors
item
cat(item, "is yummy.\n")
cat("Everyone likes", item, ".\n")
cat("I hope we have", item, "for supper!\n\n")
= position + 1 # new line
position
}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.
34.7 You CANNOT use a for loop for some problems
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)
<- function(low=1, high=10){
guessingGame <- sample(low:high, 1)
num <- 1
numGuesses <- as.numeric( readline( paste0(
guess "guess a number between ", low, " and ", high, ": " )))
while(guess != num) {
if (guess < num){
<- as.numeric( readline("higher, guess again: ") )
guess else if (guess > num) {
} <- as.numeric( readline("lower, guess again: ") )
guess
}<- numGuesses + 1
numGuesses
} cat("You got it in", numGuesses, "guesses.")
}
#guessingGame() # run this in RStudio
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.
34.8 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.
= function( numTimes ) {
sayHello
for (num in 1:numTimes){
= readline("What is your name? ")
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 times
What is your name?
Hello .
I'm fine.
How are you doing?
What is your name?
Hello .
I'm fine.
How are you doing?
What is your name?
Hello .
I'm fine.
How are you doing?
bye bye.
34.9 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.
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
#-------------------------------------------------------------------------
= function(rows, cols){
box
for (rowNumber in rows:1){ # count down - we're showing row numbers in reverse
for (colNumber in 1:cols){ # we can count up or down - we're not using colNumber
cat(rowNumber)
}cat("\n")
}
}
#debugonce(box)
box(3, 4)
3333
2222
1111
box(4, 10)
4444444444
3333333333
2222222222
1111111111
#-------------------------------------------------------------------------
# QUESTION - rewrite the box function from the previous question
# to use nested while loops
#-------------------------------------------------------------------------
= function( rows, cols){
boxWithWhile
= rows
rowNum = 1
colNum while (rowNum >= 1){
= 1
colNum while (colNum <= cols){
cat(rowNum)
= colNum + 1
colNum
}cat("\n")
= rowNum - 1
rowNum
}
}
boxWithWhile(3,4)
3333
2222
1111
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
#-------------------------------------------------------------------------
= function(size){
triangle
for(row in 1:(size*2)){
for(col in row:1){
cat(col)
}
cat("\n")
}
}
triangle(3)
1
21
321
4321
54321
654321
triangle(5)
1
21
321
4321
54321
654321
7654321
87654321
987654321
10987654321
#-------------------------------------------------------------------------
# QUESTION - rewrite the triangle function from the previous question
# to use nested while loops
# > triangle(3)
# 1
# 21
# 321
#-------------------------------------------------------------------------
= function(size){
triangle = 1
row while(row <= size) {
= row
col while(col >= 1) {
cat(col)
= col - 1
col
}
cat("\n")
= row + 1
row
}
}
triangle(3)
1
21
321
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
#-------------------------------------------------------------------------
= function(size){
triangle # FILL IN THE CODE
}
34.10 & vs && and | vs || (we already covered this)
############################################################################.
# & vs &&
# | vs ||
#
# There are two type of "and" operators and two types of "or" operators
# in R.
# The 1st type is & and |.
#
# The 2nd type is && and || (with doubled symbols).
#
# It is preferable to use the "doubled" operators for
# conditions in both "if statements" and in "while loops".
# The reason is based on the following two fundamental differences between the
# single symbol operators & and | vs the double symbols operators && and ||
#
#
# *** FIRST DIFFERENCE ***
#
# & and | are "vectorized operators" while && and || are not vectorized
#
# The single & and | are vectorized (i.e. they will return an entire
# vector of multiple TRUE/FALSE values if necessary). In the following
# example, there are two values in the answer
#
# > c(TRUE, TRUE) & c(FALSE, TRUE)
# [1] FALSE TRUE
#
# By contrast, the double && and || operators only return
# the first TRUE or FALSE
#
# > c(TRUE, TRUE) && c(FALSE, TRUE)
# [1] FALSE
#
#
# *** SECOND DIFFERENCE ***
# && and || are "shortcut operators", & and | are not.
#
# The double && and || will only process the values up until an answer
# can be figured out. We call these shortcut operators.
#
# By contrast, the single & and | will process ALL values
#
# See examples below.
##########################################################################.
Example 1
#------------------------------.
# Examples for difference #1
#------------------------------.
# & and | return multiple TRUE/FALSE values
# (i.e. & and | are "vectorized" operators)
c(TRUE,TRUE,TRUE) | c(FALSE, TRUE, FALSE)
[1] TRUE TRUE TRUE
c(TRUE,TRUE,TRUE) & c(FALSE, TRUE, FALSE)
[1] FALSE TRUE FALSE
# The double operators only return the first value for the answer.
#
# This is what you want for the conditions in "if statements" and
# in "while loops" since the entire condition should evaluate
# to a single TRUE or FALSE
c(TRUE,TRUE,TRUE) || c(FALSE, TRUE, FALSE)
Error in c(TRUE, TRUE, TRUE) || c(FALSE, TRUE, FALSE): 'length = 3' in coercion to 'logical(1)'
c(TRUE,TRUE,TRUE) && c(FALSE, TRUE, FALSE)
Error in c(TRUE, TRUE, TRUE) && c(FALSE, TRUE, FALSE): 'length = 3' in coercion to 'logical(1)'
Example 2 - isWholeNumber
#------------------------------.
# Examples for difference #2
#------------------------------.
# && and || use "shortcut" logic:
#
# FALSE && ANYTHING # The answer MUST BE FALSE
#
# TRUE && ______ # I don't konw the answer until I analyze the ______
#
#
#
#
# TRUE || ANYTHING # The answer MUST BE TRUE
#
# FALSE || ______ # I don't konw the answer until I analyze the ______
# In the following code, the trunc will not be processed if
# is.numeric is FALSE. This is appropriate since trunc would fail
# with an error if it is passed a character value.
= function( x ){
isWholeNumber = is.numeric(x) && trunc(x) == x
tf return (tf)
}
isWholeNumber(3.5) # FALSE
[1] FALSE
isWholeNumber(3) # TRUE
[1] TRUE
isWholeNumber(-3) # TRUE
[1] TRUE
isWholeNumber(-3.5) # TRUE
[1] FALSE
isWholeNumber("apple") # FALSE (this is correct)
[1] FALSE
# The following code incorrectly uses &
# Therefore the code fails with an error when passed a
# character value.
= function( x ){
isWholeNumber_bad = is.numeric(x) & trunc(x) == x
tf return (tf)
}
isWholeNumber_bad(3.5) # FALSE
[1] FALSE
isWholeNumber_bad(3) # TRUE
[1] TRUE
isWholeNumber_bad("apple") # ERROR - non-numeric argument to trunc
Error in trunc(x): non-numeric argument to mathematical function
Example 3 - is.prime
# In general when using && and || in conditions for "if statements"
# and "while loops", use the double version (i.e. && and ||
# instead of & and |)
<- function( num ) {
is.prime
# DON'T DO THE FOLLOWING:
#
# if( !is.numeric(num) | trunc(num) != num | num < 1 ){
# RATHER, DO THE FOLLOWING:
if( length(num) != 1 || !is.numeric(num) || trunc(num) != num || num < 1 ){
stop("num must be a single positive whole number")
}
if (num < 2){
return(FALSE)
}<- 2
divisor
while ( divisor <= sqrt(num) ) {
if (num %% divisor == 0){
return(FALSE)
}<- divisor + 1
divisor
}return(TRUE)
}
is.prime("apple")
Error in is.prime("apple"): num must be a single positive whole number
is.prime(c(100,200,300))
Error in is.prime(c(100, 200, 300)): num must be a single positive whole number
34.11 returning a value VS displaying a value (with cat)
#-------------------------------------------------------------------
#-------------------------------------------------------------------
# The following is a slight digression to discuss return values ...
#
# STUDENTS who are new to R are often confused about these ideas ...
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#..................................................................
# *** TL;DR *** (i.e. the main idea) ...
# (keep reading the comments below for a more lengthy explanation):
#..................................................................
#
# The countdown function does NOT return the numbers
# nor the word "blastoff!". Rather the return value of countdown() is the
# value of cat("blastoff!") function, which is invisible, and is NULL.
# Same code as above
<- function (){
countdown 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(0.25) # sleep for 0.25 seconds
}
cat("blastoff!") # after the loop has finished, display "blastoff!"
}countdown() # 10 9 8 7 6 5 4 3 2 1 blastoff!
10 9 8 7 6 5 4 3 2 1 blastoff!
<- countdown() x
10 9 8 7 6 5 4 3 2 1 blastoff!
# NULL x
NULL
#..................................................................
# *** A SOMEWHAT MORE LENGTHY EXPLANATION OF THESE CONCEPTS ***
#..................................................................
# First of all, remember that if a function doesn't execute an explicit
# return statement, then the value of the final command to be executed is returned
# from the function.
#
# Therefore, in the case of the countdown function above, what is "returned" is the
# value of cat("blastoff!") ... (but this is NOT the word "blastoff!" ... keep reading).
# The return value of cat is always NULL.
# In the next line the return value is NULL, NOT "hello".
# The return value, NULL, is then assigned to the variable x.
# Even though the word "hello" is NOT the return value,
# nevertheless, the word hello (without quotes) is displayed because cat
# will always display info to the screen.
<- cat("hello") # displays hello (without quotes) to the screen x
hello
# NULL x
NULL
# One more example ...
# Remember, the rep function repeats the VALUE of the first argument.
rep("hello", 5) # "hello" "hello" "hello" "hello" "hello"
[1] "hello" "hello" "hello" "hello" "hello"
rep(seq(1,3), 5) # 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
[1] 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3
# In the following example, the return value of cat is NULL.
# However, NULL is NOT repeated 5 times, only once.
# This is due to the special nature of the value NULL (which basically
# means "nothing"). NULL is special in that even 5 NULLs are displayed
# as a single NULL - don't worry too much about the intricacies of NULL,
# but you must know that the return value of cat is NOT "hello".
rep(cat("hello"), 5) # helloNULL
hello
NULL
rep(NULL, 5) # NULL (only once)
NULL
# Bottom line: You can use cat to display info to the screen
# but don't expect that information to be "returned" from the function.
# SOME TAKEAWAYS:
# REMEMBER:
#
# A few things to remember ...
#
# 1. If a function doesn't contain a return statement, then the
# value of the final command to be executed in the function
# is returned.
#
# 2. Based on #1 above, the value that is returned from countdown()
# is the value cat("blastoff!"). HOWEVER ... keep reading ...
#
# 3. It is very important to understand that the cat function
# does NOT "return" the information that is displayed.
#
# The purpose of cat is TO DISPLAY INFORMATION TO THE SCREEN.
# The purpose of cat is NOT to return a value.
# Programmers use the cat function to display information,
# NOT to generate a "value".The actual return value of cat
# is NULL. You never really need to use this NULL value,
# but technically, NULL is the return value.
#
# 4. You don't actually see the word NULL on the screen when you
# run the countdown function since the return value of cat is
# an "invisible" value - we explained the concept of invisible
# return values earlier in the semester when we first discussed
# the cat function.)
#-------------------------------------------------------------------
#-------------------------------------------------------------------
#
# END OF THE DIGRESSION ...
# Let's get back to discussing the for loop and the while loop
#
#-------------------------------------------------------------------
#-------------------------------------------------------------------
34.12 Use vector operations intsead of loops when possible.
# The R language has the ability to preform repetitive tasks by using
# vector operations. For example:
#
# sum(c(1,2,3,4))
#
# actually performs several different additions. This code actually calculates:
#
# total = 1
# total = total + 2
# total = total + 3
# total = total + 4
#
# the resulting total is then returned from the sum function. In most other
# languages you would need to write a loop to do this. However, because
# R has built in vector operations many times you can avoid writing loops and
# use R's built in vector operations instead.
#
# Loops in R are used for more complex situations.
# Therefore it's not so easy to come up with many simple examples
# that require loops in R. However, when practicing loops it's easier to
# work with simple examples until you master the ideas. Therefore even though
# the following examples are best accomplished with other R constructs
# you are asked to try these problems using loops instead.
34.13 — Practice —
QUESTION: splitVector
#------------------------------------------------------------
# Write a function, splitVector, that takes a single argument, vec, that
# is expected to be a vector. The function should returns a list
# that contains 3 different vectors.
#
# - The 1st vector in the list should contain the the values from vec
# that are negative
#
# - The 2nd vector in the list should contain the the values from vec
# that are between 0 and 100
#
# - The 3rd vector in the list should contain the the values from vec
# that are larger than 100
#
# PART A - answer the question without using loops
#
# PART B - answer the question using a for loop
# DO NOT USE VECTOR OPERATIONS
#
# PART C - answer the question using a while loop
# DO NOT USE VECTOR OPERATIONS
#------------------------------------------------------------
QUESTION: myUnlist(SOME_LIST)
#------------------------------------------------------------
# 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.
#------------------------------------------------------------
= function( lst ) {
myUnlist
= vector()
answer
for( listEntry in lst ){
= c(answer, listEntry)
answer
}
answer
}
<- data.frame(student=c("joe", "sue"),
students test1= c(100,90),
test2 = c(85, 95),
honors = c(FALSE, TRUE), stringsAsFactors = FALSE)
students
student test1 test2 honors
1 joe 100 85 FALSE
2 sue 90 95 TRUE
myUnlist(students)
[1] "joe" "sue" "100" "90" "85" "95" "FALSE" "TRUE"
<- function ( lst ){
myUnlist <- character(0)
answer <- 1
position while ( position <= length(lst) ) {
<- c( answer , lst[[position]] )
answer <- position + 1
position
}
return(answer)
}
<- data.frame(student=c("joe", "sue"),
students test1= c(100,90),
test2 = c(85, 95),
honors = c(FALSE, TRUE), stringsAsFactors = FALSE)
students
student test1 test2 honors
1 joe 100 85 FALSE
2 sue 90 95 TRUE
myUnlist(students)
[1] "joe" "sue" "100" "90" "85" "95" "FALSE" "TRUE"
#------------------------------------------------------------
# Rewrite myUnlist to use a for loop
#------------------------------------------------------------
<- function ( lst ){
myUnlist <- character(0)
answer for( value in lst ){
<- c(answer , value)
answer
}return(answer)
}
<- data.frame(student=c("joe", "sue"),
students test1= c(100,90),
test2 = c(85, 95),
honors = c(FALSE, TRUE), stringsAsFactors = FALSE)
students
student test1 test2 honors
1 joe 100 85 FALSE
2 sue 90 95 TRUE
myUnlist(students)
[1] "joe" "sue" "100" "90" "85" "95" "FALSE" "TRUE"
QUESTION: dfToVec - only character columns
#------------------------------------------------------------
# Modify the code for dfToVec so that the vector
# that is returned ONLY INCLUDES the values that
# are in character columns of the dataframe.
#------------------------------------------------------------
<- function ( df ){
dfToVec <- character(0)
answer
for ( column in df ) {
if ( is.character(column) ){
<- c(answer, column)
answer
}
}
return ( answer )
}
<- data.frame(student=c("joe", "sue"),
students test1= c(100,90),
test2 = c(85, 95),
year = c("fr", "so"),
honors = c(FALSE, TRUE), stringsAsFactors = FALSE)
students
student test1 test2 year honors
1 joe 100 85 fr FALSE
2 sue 90 95 so TRUE
dfToVec(students)
[1] "joe" "sue" "fr" "so"
QUESTION: dfToVec - only positive numbers, TRUE values and words that start with “a”
#------------------------------------------------------------
# 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
#------------------------------------------------------------
<- function( df ){
dfToVec
<- character(0)
answer
for ( column in df ) { # start of "outer" for loop
for ( value in column) { # start of "inner" for loop
if( is.character(value) ){
if ( value >= "a" & value < "b" ){
<- c(answer, value)
answer
}else if ( is.numeric(value)) {
} if( value > 0){
<- c(answer, value)
answer
}else if (is.logical(value) ) {
} if (value == TRUE){
<- c(answer, value)
answer
}
}
# end of "inner" for loop
}
# end of "outer" for loop
} return(answer)
}
<- data.frame(student=c("joe", "alice", "mike", "anne"),
students test1= c(100,90,-20,-30),
test2 = c(-10, -40, 85, 95),
favFood = c("orange", "pear", "apple", "artichoke"),
honors = c(TRUE, FALSE, FALSE, FALSE), stringsAsFactors = FALSE)
students
student test1 test2 favFood honors
1 joe 100 -10 orange TRUE
2 alice 90 -40 pear FALSE
3 mike -20 85 apple FALSE
4 anne -30 95 artichoke FALSE
dfToVec(students) # "alice" "anne" "100" "90" "85" 95" "TRUE"
[1] "alice" "anne" "100" "90" "85" "95"
[7] "apple" "artichoke" "TRUE"
#------------------------------------------------------------
# another way
#------------------------------------------------------------
# loop through the column numbers (outer loop)
# loop through the row numbers (inner loop)
# check a particular value from a given row,column
<- function( df ){
dfToVec
<- character(0)
answer
for ( colNumber in 1:ncol(df)) { # "outer" for loop
for ( rowNumber in 1:nrow(df)) { # "inner" for loop
= df[rowNumber, colNumber] # get a single value
value
if( is.character(value) ){
if ( value >= "a" & value < "b" ){
<- c(answer, value)
answer
}else if ( is.numeric(value)) {
} if( value > 0){
<- c(answer, value)
answer
}else if (is.logical(value) ) {
} if (value == TRUE){
<- c(answer, value)
answer
}
}
# end of "inner" for loop
}
# end of "outer" for loop
}
return(answer)
}
<- data.frame(student=c("joe", "alice", "mike", "anne"),
students test1= c(100,90,-20,-30),
test2 = c(-10, -40, 85, 95),
favFood = c("orange", "pear", "apple", "artichoke"),
honors = c(TRUE, FALSE, FALSE, FALSE), stringsAsFactors = FALSE)
students
student test1 test2 favFood honors
1 joe 100 -10 orange TRUE
2 alice 90 -40 pear FALSE
3 mike -20 85 apple FALSE
4 anne -30 95 artichoke FALSE
dfToVec(students) # "alice" "anne" "100" "90" "85" 95" "TRUE"
[1] "alice" "anne" "100" "90" "85" "95"
[7] "apple" "artichoke" "TRUE"
34.14 *** IMPORTANT CONCEPTS ABOUT FOR LOOPS ***
#####################################################################
#####################################################################
##
## *** IMPORTANT CONCEPTS ABOUT FOR LOOPS ***
##
## 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.
##
##
##
##
##
##
## *** 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.
##
##############################################################
##############################################################
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
#--------------------------------------------------------------
# The following info about strsplit will help with your answer
strsplit function
#...............
# strsplit
#...............
# strsplit will split up each value in a
# character vector into multiple values.
#
# IMPORTANT: The return value of strsplit is a LIST.
?strsplit
starting httpd help server ... done
# multiple values in the original vector
= c("Cohen,Sam","Jones,Bob","Andrews,Claire")
people people
[1] "Cohen,Sam" "Jones,Bob" "Andrews,Claire"
length(people)
[1] 3
<-strsplit(people,",")
splitPeople 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"
3]][2] splitPeople[[
[1] "Claire"
# use a colon as a separator
<- c("New York:NY:USA", "Jerusalem::IL", "LA:CA:USA", "Miami:FL:USA")
places 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
= c("apple,orange,pear")
fruit fruit
[1] "apple,orange,pear"
length(fruit)
[1] 1
<- strsplit(fruit, ",")
splitFruit splitFruit
[[1]]
[1] "apple" "orange" "pear"
# display just the 2nd fruit (notice the [[double-brackets]][single-brackets])
1]][2] splitFruit[[
[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
= c("racecar", "pineapple", "cart")
words 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.
<- "racecar"
word <- strsplit(word,"")
y y
[[1]]
[1] "r" "a" "c" "e" "c" "a" "r"
1] # still a list y[
[[1]]
[1] "r" "a" "c" "e" "c" "a" "r"
1]] # the vector without the surrounding list y[[
[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
1]][2] # one way y[[
[1] "a"
unlist(y)[2] # another way
[1] "a"
<- y[[1]] # you could use a variable
z 2] z[
[1] "a"
isPalindrome(x) with a while loop
#.........................................................
#
# Let's get back to coding the isPalindrome function ...
#
# ... first let's do it with a while loop
#
#.........................................................
<- function(x) {
isPalindrome if (!is.character(x) | length(x) !=1){
stop("x must have a single character value")
}
<- strsplit(x, "")[[1]] # get the individual letters in a vector
lets
= 1
left = length(lets)
right
while ( left < right) {
if(lets[left] != lets[right]){
return(FALSE)
}= left + 1
left = right - 1
right
}
return(TRUE)
}
isPalindrome("abcdxba") # FALSE
[1] FALSE
isPalindrome("abcdcba") # TRUE
[1] TRUE
isPalindrome("racecar") # TRUE
[1] TRUE
isPalindrome("amanaplanacanalpanama") # TRUE
[1] TRUE
isPalindrome("a man a plan a canal panama") # FALSE - because of the spaces
[1] FALSE
#.........................................................
# Rewrite the function so that it removes spaces before
# checking to see if x is a palindrome
#.........................................................
<- function(x) {
isPalindrome if (!is.character(x) | length(x) !=1){
stop("x must have a single character value")
}
<- strsplit(x, "")[[1]]
lets
<- lets [ lets != " " ] # get rid of the spaces from lets
lets
= 1
left = length(lets)
right
while ( left < right) {
if(lets[left] != lets[right]){
return(FALSE)
}= left + 1
left = right - 1
right
}
return(TRUE)
}
isPalindrome("a man a plan a canal panama") # TRUE
[1] TRUE
isPalindrome(x) with a for loop
#................................................
#
# Rewrite the code to use a for loop
#
#................................................
<- function(x) {
isPalindrome
if (!is.character(x) | length(x) !=1){
stop("x must have a single character value")
}
<- strsplit(x, "")[[1]] # get the individual letters in a vector
lets
# get rid of the spaces from lets
<- lets [ lets != " " ]
lets
for ( num in 1:trunc(length(lets)/2) ){
# note: num will start at 1, then 2, then 3, up until 1/2 the length of the word
# exmpale: for "racecar" num will be 1 then 2 then 3 then stop
= num
first = length(lets)-num+1
last if( lets[first] != lets[last]){
return(FALSE)
}
}return(TRUE)
}
isPalindrome("abcdxba") # FALSE
[1] FALSE
isPalindrome("abcdcba") # TRUE
[1] TRUE
isPalindrome("racecar") # TRUE
[1] TRUE
isPalindrome("amanaplanacanalpanama") # TRUE
[1] TRUE
isPalindrome("a man a plan a canal panama") # TRUE
[1] TRUE