34  32. “if / else if / else” constructs

34.1 Various forms of if statement

#################################################
#################################################
##
## Various forms of if statement
##
#################################################
#################################################

34.2 if by itself - (no else, no else if)

#-----------------------------------------------------------------.
# if by itself - (no else, no else if)
#-----------------------------------------------------------------.
# An "if" is written with the following format. See below for an
# explanation:
#
#   R commands before if
#   R commands before if
#   R commands before if
#
#   if ( LOGICAL_EXPRESSION_RESULTING_IN_TRUE_OR_FALSE ) {
#     R commands inside if
#     R commands inside if
#     R commands inside if
#   }
#
#   R commands after if
#   R commands after if
#   R commands after if
#
# EXPLANATION: 
#
#   The commands before the if are processed as they normally would be.
#
#   When the code reaches the "if" the LOGICAL_EXPRESSION is evaluated.
#   After the logical expression are several commands inside of {curly braces}.
#   The code inside the {curly braces} is known as a "block of code".
#   If the result of the LOGICAL_EXPRESSION is TRUE then the R commands inside
#   the {curly braces} are done.
#   If the result of the LOGICAL_EXPRESSION is FALSE then the R commands inside
#   the {curly braces} are SKIPPED.
#
#   The commands after the if are processed as they normally would be.
#-----------------------------------------------------------------.

rm(list=ls())   # start from scratch

commentsAboutNumber = function( num ){
  cat ("You passed the number", num, "to this function.\n")
  
  if (num > 1000) {
    cat("Wow! that's a big number.\n")
    cat ("I LOVE big nubmers.\n")
    cat("Big numbers are BEAUTIFUL.\n")
  }
  
  cat("Thanks for your number :)\n")
}


commentsAboutNumber(10)
You passed the number 10 to this function.
Thanks for your number :)
commentsAboutNumber(99999)
You passed the number 99999 to this function.
Wow! that's a big number.
I LOVE big nubmers.
Big numbers are BEAUTIFUL.
Thanks for your number :)

34.3 {Curly braces} are technically only required for 2 or more commands

#-----------------------------------------------------------------
# The curly braces are not "actually" required if there is only 
# one command - but I recommend you use them anyway, until you 
# get more comfortable with the language.
#-----------------------------------------------------------------
commentsAboutNumber = function( num ){
  
  cat ("You passed the number", num, "to this function.\n")

  # This works without the {curly braces} since there's only 
  # one command in the if
  
  if (num > 1000) 
    cat("Wow! that's a big number.\n")
  
  cat("Thanks for your number :)\n")
}

commentsAboutNumber(10)
You passed the number 10 to this function.
Thanks for your number :)
commentsAboutNumber(99999)
You passed the number 99999 to this function.
Wow! that's a big number.
Thanks for your number :)

34.4 DON’T FORGET the {curly braces} if they are needed.

# MAKE SURE YOU DON'T FORGET THE {curly braces}
commentsAboutNumber = function( num ){

  cat ("You passed the number", num, "to this function.\n")
  
  # THIS IS WRONG - the {curly braces} are missing ...
  # THIS WILL NOT DO WHAT YOU THINK!!!
  if (num > 1000) 
    cat("Wow!", num, "is a big number.\n") # This line IS controlled by if
    cat ("I LOVE big nubmers.\n")          # This line is NOT controlled by if
    cat("Big numbers are BEAUTIFUL.\n")    # This line is NOT controlled by if

  cat("Thanks for your number :)\n")
}

commentsAboutNumber(10)
You passed the number 10 to this function.
I LOVE big nubmers.
Big numbers are BEAUTIFUL.
Thanks for your number :)
commentsAboutNumber(99999)
You passed the number 99999 to this function.
Wow! 99999 is a big number.
I LOVE big nubmers.
Big numbers are BEAUTIFUL.
Thanks for your number :)

34.5 EXAMPLE

#..................................................................
# Function should return the price of a salad based on the size
#
#    small  (4 choices) : 9
#    medium (5 choices) : 12
#    large  (6 choices) : 14
#    xlarge (7 choices) : 15
#    any other size - stop with a meaningful error
#..................................................................

priceOfSalad = function(size) {
  
  # check at the beginning for one of the correct sizes
  if ( ! size %in% c("small", "medium", "large", "xlarge")) {
    stop("size must be one of : small , medium, large, xlarge")
  }
  
  if (size == "small") {
    price = 9
  }
  
  if (size == "medium"){
    price = 12
  } 
  
  if (size == "large") {
    price = 14
  } 
  
  if (size == "xlarge") {
    price = 15
  }
  
  price   # return the total price   
}

priceOfSalad("medium")
[1] 12
priceOfSalad("small")
[1] 9
priceOfSalad("gargantuan")   # ERROR none of the prices were assigned
Error in priceOfSalad("gargantuan"): size must be one of : small , medium, large, xlarge
# use the debugger to see how R steps through the code

#debugonce(priceOfSalad)
priceOfSalad("medium")
[1] 12
#debugonce(priceOfSalad)
priceOfSalad("small")
[1] 9
#debugonce(priceOfSalad)
priceOfSalad("large")
[1] 14

34.6 Be careful with parentheses and {curly braces} !

#.............................................................
# Be careful with parentheses and {curly braces} !
#
# If you position the cursor right after
# a (parentheses) or {curly brace} or [square-brackets], the matching
# open/close (parentheses) or {curly brace} or [square-brackets] will
# be highlighted in grey. It's hard to see at first but look for it.
# It should happen.
#
# Try editing the above function by adding and/or removing
# one or more (parentheses) or {curly braces} and then find the
# new matching open/close. This should give you practice trying to 
# find a missing or extra open or close (parentheses) or {curly brace}
# when you need to.
#.............................................................

34.7 It’s possible that NO if will run!

#--------------------------------------------------------------------
# It's possible that NO if will run!
#--------------------------------------------------------------------

# The following line will result in an error. 
# This is because, "gargantuan" is not one of the values that are 
# expected for the size of the salad. 

priceOfSalad("gargantuan")   # ERROR object price not found 
Error in priceOfSalad("gargantuan"): size must be one of : small , medium, large, xlarge
# You can use the debugger to see exactly why we get the error. 
#debugonce(priceOfSalad)

priceOfSalad("gargantuan")   # ERROR object price not found 
Error in priceOfSalad("gargantuan"): size must be one of : small , medium, large, xlarge

34.8 Using the “stop” function to check arguments to a function

# The error above occurred because the call to the function passed 
# "gargantuan" as the size. However, the code in the function did 
# not anticipate such a size. The error message was a little hard 
# to understand. It would have been much better if a more helpful 
# error message were displayed. This can be accomplished with the stop
# function.
#
# The stop( SOME_ERROR_MESSAGE ) function can be used to display
# a more appropriate error message as shown in the following example.
#
# The following version of the function includes a check
# at the beginning of the code. If the size argument doesn't contain
# one of the expected sizes then a meaningful error message is
# displayed. See the next section for a more detailed discussion of 
# the stop function.

priceOfSalad = function(size) {
  
  # check at the beginning for one of the correct sizes
  if ( ! size %in% c("small", "medium", "large", "xlarge")) {
    stop("size must be one of : small , medium, large, xlarge")
  }
  
  if (size == "small") {
    price = 9
  }
  
  if (size == "medium"){
    price = 12
  } 
  
  if (size == "large") {
    price = 14
  } 
  
  if (size == "xlarge") {                          # an else DOES NOT HAVE a condition
    price = 15
  }
  
  price   # return the total price   
}

priceOfSalad("medium")
[1] 12
priceOfSalad("small")
[1] 9
priceOfSalad("gargantuan") # ERROR: size must be one of small, medium, large, xlarge
Error in priceOfSalad("gargantuan"): size must be one of : small , medium, large, xlarge

34.9 stop(“SOME ERROR MESSAGE”)

############################################################################
# stop("SOME ERROR MESSAGE")
#
# The stop function stops the execution of a function and displays
# an error message. 
############################################################################

# The following function adds two square roots. 
# However, if either num1 or num2 are passed negative numbers, the
# function will not work as expected. It will display some confusing
# messages and return NaN. This is not ideal. 

addSquareRoots = function(num1, num2){
  return (sqrt(num1) + sqrt(num2) )
}

addSquareRoots(49, 4)
[1] 9
addSquareRoots(-49,-2)   # Warning: NaNs produced ... etc.
Warning in sqrt(num1): NaNs produced
Warning in sqrt(num2): NaNs produced
[1] NaN
# This is why you should always check the values being passed into a function
# to make sure that they are appropriate for the function. If the values
# being passed into the function aren't appropriate then you can stop
# the function with a better error message.

# In the code below the stop will only happen if either num1 is less
# than zero or num2 is less than zero

addSquareRoots = function(num1, num2){
  if ( num1 < 0  |  num2 < 0  )  {
    stop("you specified a negative number. num1 and num2 may not be negative")
  }
  
  sqrt(num1) + sqrt(num2)
}

addSquareRoots(-49, 4)   # stop with an ERROR 
Error in addSquareRoots(-49, 4): you specified a negative number. num1 and num2 may not be negative
addSquareRoots(49, -4)   # stop with an ERROR
Error in addSquareRoots(49, -4): you specified a negative number. num1 and num2 may not be negative
addSquareRoots(49, 4)    # This time it works
[1] 9
addSquareRoots("apple","orange")
Error in sqrt(num1): non-numeric argument to mathematical function
# Modify addSquareRoots to also check for non-numeric arguments
addSquareRoots = function(num1, num2){
 
 # check for non-numeric arguments
 if ( is.numeric(num1) == FALSE | is.numeric(num2) == FALSE){
  stop("You specified a non numeric value. num1 and num2 must be numeric values.")
 }
 
 # check for negative numbers
 if ( num1 < 0  |  num2 < 0  )  {
  stop("you specified a negative number. num1 and num2 may not be negative")
 }
 
 sqrt(num1) + sqrt(num2)
}

#debugonce(addSquareRoots)  # debug this to figure out what's going on
addSquareRoots(49,"orange")
Error in addSquareRoots(49, "orange"): You specified a non numeric value. num1 and num2 must be numeric values.
#debugonce(addSquareRoots)
addSquareRoots(-49,-4)
Error in addSquareRoots(-49, -4): you specified a negative number. num1 and num2 may not be negative
#debugonce(addSquareRoots)
addSquareRoots(49, 4)
[1] 9
addSquareRoots(-2,"orange")
Error in addSquareRoots(-2, "orange"): You specified a non numeric value. num1 and num2 must be numeric values.
addSquareRoots("apple",4)
Error in addSquareRoots("apple", 4): You specified a non numeric value. num1 and num2 must be numeric values.
#debugonce(addSquareRoots)
addSquareRoots(-49,-4)
Error in addSquareRoots(-49, -4): you specified a negative number. num1 and num2 may not be negative

When a function is stopped no value is returned

#---------------------------------------------------------
# When a function is stopped no value is returned
#---------------------------------------------------------

answer = addSquareRoots(49, -4)   # stop with an ERROR
Error in addSquareRoots(49, -4): you specified a negative number. num1 and num2 may not be negative
answer   # ERROR - answer has no value
Error in eval(expr, envir, enclos): object 'answer' not found
answer = addSquareRoots(49, 4)    # This time it works
answer   # 9
[1] 9
answer = addSquareRoots(-1 , -1)   # stop with an ERROR
Error in addSquareRoots(-1, -1): you specified a negative number. num1 and num2 may not be negative
answer   # 9 - it never changed since the previous line ended with an error
[1] 9
# In general, I recommend (and require for homeworks) that you check all
# of the arguments being passed into your functions to make sure that the
# values make sense for what the function is supposed to do. 
# For example, if an argument is supposed to be numeric, you should stop
# the function with an error if it is not numeric. (see more examples below)

34.10 — PRACTICE —

######################################################################.
# QUESTION
# 
# Write a function called myabs. The function should take an 
# argument named num that is expected to be passed a single number.
# The function should return the absolute value of that number. 
# However, do NOT use the abs function to write your function. 
#
# (a) Write the function using an if statement.
#
# (b) write the function using the ifelse() function
#
######################################################################.

###########.
# ANSWER
###########.

# YOUR CODE GOES HERE

34.11 INTRODUCING “else if” AND “else”

#...............................................................
#
# INTRODUCING "else if" AND "else" 
#
#   if (condition1)     { ... }
#   else if(condition2) { ... }
#   else if(condition3) { ... }
#   etc ...                       # as many "else if" sections as you like
#   else { ... }                  # no condition on an "else" section!!!
#
# The code in the block for the first condition that is TRUE will run.
# NO OTHER BLOCKS OF CODE WILL RUN.
# 
#...............................................................

# In code (as below) that is structured as 
# if / else if / else if / ... etc ... / else
# EXACTLY ONE AND ONLY ONE of the blocks of code will happen.

priceOfSalad = function(size) {
  
  # EXACTLY ONE AND ONLY ONE of the following sections will execute.

  if (size == "small") {
    price = 9
    
  } else if (size == "medium") {
    price = 12
    
  } else if (size == "large") {
    price = 14
    
  } else if (size == "xlarge") {            
    price = 15
    
  } else {  # if nothing else happened, this happens. An "else" does NOT have a condition
    stop("size must be one of : small , medium, large, xlarge")
  }
  
  price   # return the total price   
}

priceOfSalad("medium")
[1] 12
priceOfSalad("small")
[1] 9
priceOfSalad("gargantuan")   # ERROR - "size must be one of : small , medium, large, xlarge"
Error in priceOfSalad("gargantuan"): size must be one of : small , medium, large, xlarge
# another version ...
# This one works but doesn't display a "nice" error message when size is not valid

priceOfSalad = function(size) {
  
  # EXACTLY ONE AND ONLY ONE of the following sections will execute.
  
  if (size == "small") {
    price = 9
    
  } else if (size == "medium") {
    price = 12
    
  } else if (size == "large") {
    price = 14
    
  } else if (size == "xlarge") {            
    price = 15
    
  } 
  
  price   # return the total price   
}

priceOfSalad("medium")
[1] 12
priceOfSalad("small")
[1] 9
priceOfSalad("gargantuan")   # ERROR - object 'price' not found
Error in priceOfSalad("gargantuan"): object 'price' not found
# one more version - without an else
#
# If you specify incorrect size, then 
# price is $0 and you don't get any salad
priceOfSalad = function(size) {

  # if you don't specify a correct size, the price will be this:
  price = 0
  
  # if the size were not small, medium, large or xlarge
  # we would not have gotten this far. 
  
  if (size == "small") {
    price = 9
    
  } else if (size == "medium") {
    price = 12
    
  } else if (size == "large") {
    price = 14
    
  } else if (size == "xlarge") {  
    price = 15  
  }
  
  # THERE WAS NO "else".
  #
  # If none of the conditions were TRUE then none of the "if" or "else if"
  # sections above would have been run. 
  #
  # However, since we set the price to zero before the very first "if", 
  # when none of the "if" / "else if" sections run, the price will be zero.
  
  price
}

priceOfSalad("large")        # 14
[1] 14
priceOfSalad("gargantuan")   # 0 ... you don't have to pay, but you don't get a salad
[1] 0

34.12 “else if” sections that are arranged in a specific order

#-----------------------------------------------------------------------------.
# Sometimes the order that your write the different conditions makes a 
# difference between the code working as planned and not working at all.
#
# The following example makes use of "else if" sections that are arranged
# in a very specific order. Remember that the FIRST condition that is TRUE
# will cause that section of code to execute and NO OTHER sections of code
# after that will execute.
#-----------------------------------------------------------------------------.


# Admission to amusement park based on age
#
#    65 and up :  30
#    13 - 64:     40
#    4 - 13:      25
#    0 - 3:       0 (ie. free)


admissionPrice = function(age){
  
  # EXACTLY ONE OF THE PRICES WILL BE ASSIGNED
  if (age >= 65)
    price = 30
  
  else if (age >= 13)
    price = 40
  
  else if (age >= 4) 
    price = 25
  
  else            # DO NOT USE ANY CONDITION WITH A FINAL ELSE 
    price = 0
  
  price
}

admissionPrice(70)
[1] 30
admissionPrice(23)
[1] 40
admissionPrice(2)
[1] 0
# WHY DOES THE FOLLOWING RETURN 30 ??? 
admissionPrice("apple")
[1] 30
# USE THE DEBUGGER TO FIGURE IT OUT
#debugonce(admissionPrice)
admissionPrice("apple")
[1] 30
# ANOTHER ATTEMPT AT THE SAME PROBLEM:
# THIS WILL NOT WORK CORRECTLY !!!
# TRY TO UNDERSTAND WHY NOT.

admissionPrice = function(age){
  
  if (age >= 65) {
    price = 30
  }
 
  if (age >= 13) {
    price = 40
  }

  if (age >= 4) {
    price = 25
  }

  # THIS WILL ALWAYS HAPPEN - FOR ANY AGE !!!
  if (age >= 0) {
    price = 0
  }

  price
}

admissionPrice(70)   # 0
[1] 0
admissionPrice(25)   # 0
[1] 0
# You can reverse the order of the if's but that is NOT recommended
# since you are causing R to do extra work. You are also causing someone
# who is reading your program to think through extra logic. This
# is NOT a good approach. Best approach is to use "else if" in 
# a situation where only one section of code needs to run.
# 2nd ATTEMPT TO USE SEPARATE IFs.
#
# This WILL work correctly but IS NOT RECOMMENDED.
#
# *** REMEMBER - THE BEST APPROACH IF YOU HAVE ONLY ONE BLOCK OF CODE ***
# *** THAT NEEDS TO EXECUTE IS TO USE "else if" SECTIONS.             ***

admissionPrice = function(age){
  
  price = 0
  
  if (age >= 4)    
    price = 25     # this WILL happen if the age is 70
  
  if (age >= 13)
    price = 40     # this will ALSO happen if the age is 70
  
  if (age >= 65)   
    price = 30     # this will ALSO happen if the age is 70
  
  price #the final price for age==70 will be 30, but price was set unnecessarily 3 different times
}

# This works but use the debugger to see why it is not ideal.
# You are making R do extra work.

admissionPrice(70) # 30: correct result but not ideal approach. price is set 3 different times
[1] 30
admissionPrice(23) # 40: correct result but not ideal approach. price is set 3 different times
[1] 40
admissionPrice(2) # 40: correct result but not ideal approach. price is set 3 different times
[1] 0
# 3rd ATTEMPT TO USE SEPARATE IFs.
#
# This WILL work correctly but IS ALSO NOT RECOMMENDED.
#
#
# *** REMEMBER - THE BEST APPROACH IF YOU HAVE ONLY ONE BLOCK OF CODE ***
# *** THAT NEEDS TO EXECUTE IS TO USE "else if" SECTIONS.             ***


admissionPrice = function(age){
  
  price = 0
  
  if (age >= 4 & age < 13)    # code is overly complex and error prone
    price = 25
  
  if (age >= 13 & age < 65)   # code is overly complex and error prone 
    price = 40
  
  if (age >= 65)   
    price = 30
  
  price
}

admissionPrice(70)
[1] 30
admissionPrice(23)
[1] 40
admissionPrice(2)
[1] 0
#------------------------------------------
# BE CAREFUL !!! 
#------------------------------------------

# 4th ATTEMPT TO USE SEPARATE IFs.
#
# This does NOT work correctly!!!
# Don't do this.
#
# *** REMEMBER - THE BEST APPROACH IF YOU HAVE ONLY ONE BLOCK OF CODE ***
# *** THAT NEEDS TO EXECUTE IS TO USE "else if" SECTIONS.             ***


admissionPrice = function(age){
  
  price = 0
  
  if (age >= 4 & age < 13){    # this is NOT connected with the next if
    price = 25
  }
  
  if (age >= 13 & age < 65){   # this IS connected to the following else
    price = 40
    
  } else {        # THIS LOGIC IS WRONG - this will happen for 5-year-olds
    price = 30
    
  }
  
  price  # correct for adults and seniors    WRONG for children and infants
}

admissionPrice(2)   # 30 - WRONG ANSWER - use the debugger to figure out why.
[1] 30
admissionPrice(5)   # 30 - WRONG ANSWER - use the debugger to figure out why.
[1] 30
admissionPrice(20)  # 40 - correct
[1] 40
admissionPrice(70)  # 30 - correct
[1] 30
#debugonce(admissionPrice)
admissionPrice(2)   # 30 - WRONG ANSWER - use the debugger to figure out why.
[1] 30
######################################################################.
# QUESTION
# 
# Challenge - rewrite the previous code for the admissionPrice function.
# However, this time DO NOT use an if statement.
#
# (a) write the code using nested ifelse() functions.
#
# (b) Write the code by using a named vector. 
#     For this version, do NOT use any if statements and do not use 
#     any ifelse() function calls.
######################################################################.

###########.
# ANSWER
###########.

# YOUR CODE GOES HERE

34.13 A slightly more complex example - same ideas

#--------------------------------------------------------------
# A slightly more complex example - same ideas
#--------------------------------------------------------------

# size and dressing options
#
#    small  (4 choices) : 9
#    medium (5 choices) : 12
#    large  (6 choices) : 14
#    xlarge (7 choices) : 15
#    any other size - stop with an error
#
#    dressing = TRUE or FALSE :  TRUE additional .50   FALSE = 0
#    any other value stop with an error

priceOfSalad = function(size, dressing) {
  
  # check at the beginning for one of the correct sizes
  if ( ! size %in% c("small", "medium", "large", "xlarge")) {
    stop("size must be one of : small , medium, large, xlarge")
  }
  
  # check at the beginning for a valid value for dressing (should be TRUE or FALSE)
  if (! is.logical(dressing) & length(dressing) == 1){
    stop ("dressing must be TRUE or FALSE")
  } 
  
  # EXACTLY ONE OF THE FOLLOWING TWO BLOCKS OF CODE HAPPENS
  
  if (dressing) { # "if(dressing)" is the same as "if(dressing == TRUE)" 
    price = 0.50
  } else {                          # an else DOES NOT HAVE a condition
    price = 0
  }

  # STARTING A NEW "if"
  #
  # EXACTLY ONE OF THE FOLLOWING BLOCKS OF CODE HAPPENS
  
  if (size == "small") {
    price = price + 9
    
  } else if (size == "medium") {
    price = price + 12
    
  } else if (size == "large") {
    price = price + 14
    
  } else {                          # an else DOES NOT HAVE a condition
    price = price + 15
  }
  
  price   # return the total price   
}

priceOfSalad("medium", FALSE)
[1] 12
priceOfSalad("small", TRUE)
[1] 9.5
priceOfSalad("gargantuan", FALSE)
Error in priceOfSalad("gargantuan", FALSE): size must be one of : small , medium, large, xlarge
priceOfSalad("large", "caeser")
Error in priceOfSalad("large", "caeser"): dressing must be TRUE or FALSE

34.14 if ( SOME_LOGICAL_VECTOR ) # if only “looks at” the first logical value

#----------------------------------------------------------------------
# if ( SOME_LOGICAL_VECTOR ){   # open curly brace goes here  
#
#    SOME CODE                  #  This is a "block" of code
#    SOME MORE CODE
#    EVEN MORE CODE
#    ETC
#
# }                             # closing curly brace
#
# The if only "looks at" the first value in the logical vector.
#
# If the first value in the logical vector is TRUE then the 
# code in the "block of code" is done.
#
# If the first value in the logical vector is FALSE then the 
# code in the "block of code" is NOT done.
#
# In either case, the lines of code AFTER the if is done 
# whether or not the {block of code} for the if was done.
#-----------------------------------------------------------------------------

34.15 & vs &&     and     | vs ||

########################################################################.
# In (conditions) of "if"s use && instead of & and use || instead of | 
########################################################################.
# This topic is relevant for the following R constructs, not just "if": 
# 
#  - if statements
# 
#  - while loops
# 
#  - for loops
#
# Up until now, we have been using the operators & and |.
# However, in the (conditions) of "if"s you should use
# && instead of & and use || instead of |. The reasons why are explained below.
#
# & and | operators are "vectorized" - i.e. they work with vectors of
# any length. For example, just like c(2,3,4)+c(100,200,300) returns
# multiple answers, so too, c(TRUE,FALSE,TRUE) & c(FALSE,FALSE,TRUE)
# returns multiple answers.
#
# The "double" version of the && and || operators are NOT "vectorized" 
# operators - i.e. you may only use them with single values.
# 
# SEE EXAMPLES BELOW
########################################################################.

& and | are “vectorized”      && and || are NOT vectorized

###########################################.
# & is vectorized - i.e. multiple answers
###########################################.
c(TRUE, FALSE, TRUE, FALSE) & c(TRUE, TRUE, FALSE, FALSE)
[1]  TRUE FALSE FALSE FALSE
####################################################################.
# ERROR - && is NOT vectorized - it only works with single values
####################################################################.
c(TRUE, FALSE, TRUE, FALSE) && c(TRUE, TRUE, FALSE, FALSE)
Error in c(TRUE, FALSE, TRUE, FALSE) && c(TRUE, TRUE, FALSE, FALSE): 'length = 4' in coercion to 'logical(1)'
# These examples work as expected:
FALSE && FALSE
[1] FALSE
FALSE && TRUE
[1] FALSE
TRUE && FALSE
[1] FALSE
TRUE && TRUE
[1] TRUE
#######################.
# Same with | vs ||
#######################.

# | is vectorized - i.e. multiple answers
c(TRUE, FALSE, TRUE, FALSE) | c(TRUE, TRUE, FALSE, FALSE)
[1]  TRUE  TRUE  TRUE FALSE
# ERROR - || is NOT vectorized - it only works with single values
c(TRUE, FALSE, TRUE, FALSE) || c(TRUE, TRUE, FALSE, FALSE)
Error in c(TRUE, FALSE, TRUE, FALSE) || c(TRUE, TRUE, FALSE, FALSE): 'length = 4' in coercion to 'logical(1)'
# These examples work as expected:
FALSE || FALSE
[1] FALSE
FALSE || TRUE
[1] TRUE
TRUE || FALSE
[1] TRUE
TRUE || TRUE
[1] TRUE

&& and || are “short circuit” versions of & and |

#############################################################################.
#
# THIS WILL BE EXPLAINED IN CLASS ON THE BOARD. THE NOTES ABOUT THIS 
# ARE IN THE SECTION ON "for loops". I WILL MOVE THAT INFORMATION HERE
# LATER.
#
# - Prof. Rosenthal
#
#############################################################################.

In conditions of if statements use && instead of & and use || instead of |

##############################################################################.
# The logical condition in an "if" only expects one TRUE/FALSE value. Therefore
# it is highly recommended that you use && instead of & and || instead of |.
#
# Also, by using && and || you can write more concise code by taking 
# advantage of the short-circuit nature of && and ||.
#
# (see examples below)
#
# The same will be true for for loops and while loops (which will be covered
# very soon)
##############################################################################.

34.16 Different ways of writing the same logic

#-----------------------------------------------------------
# The following illustrates two concepts
# 
# - Using multiple separate "if"s to build up an answer 
#
# - "Nested" if's (i.e. an if inside of another if)
#-----------------------------------------------------------

# Write a function that determines qualities of a number
# e.g. whole number, even, odd, positive, negative, zero, perfect square, etc.

34.17 One solution - Using multiple separate “if”s to build up an answer

# The following version just uses if (there is no "else" in this version)

describeNumber <- function(n) {
  
  # make sure that n is a single number and stop if it isn't
  
  if (!is.numeric(n) | length(n) != 1){
    stop ("n must be a single number")
  }
  
  # start with an empty character vector 
  answer <- character(0)   # empty answer so far
  
  # Each of the following checks to see if the number meets some condition.
  #
  # If the condition is TRUE, then a new description of the number
  # is added to the answer.
  #
  # If the condition is FALSE, then nothing happens with that if and 
  # the next if is checked.
  
  if (trunc(n) == n) {
    answer <- c(answer, "whole number")
  } 

  if (trunc(n) != n) {
    answer <- c(answer, "not a whole number")
  }

  if(n%%2 == 0){
    answer <- c(answer, "even")   
  } 
  
  if(n%%2 == 1){
    answer <- c(answer, "odd")   
  } 
  
  if (n > 0){
    answer <- c(answer, "positive")   
  } 
  
  if (n < 0) {
    answer <- c(answer, "negative")   
  } 
  
  if (n==0) {
    answer <- c(answer, "zero")
  }
  
  # If you take the sqrt of a negative number R will 
  # give you an error. 
  #
  # Make sure that we don't run the sqrt function unless
  # we know that n is not negative.
  if (n>=0){
    
    # If we got this far then we know that n is not negative so we 
    # can use the sqrt(n) function.
    #
    # An if inside of an if is known as a "nested if"
    
    if (sqrt(n) == trunc(sqrt(n))){
      
      # n is not negative and is a perfect square
      answer <- c(answer, "perfect square")
    }
    
  }
  
  
  return (answer)  
}

describeNumber("apple")
Error in describeNumber("apple"): n must be a single number
describeNumber(c(10,20))
Error in describeNumber(c(10, 20)): n must be a single number
describeNumber(2.5)
[1] "not a whole number" "positive"          
describeNumber(7)
[1] "whole number" "odd"          "positive"    
describeNumber(-7.5)
[1] "not a whole number" "negative"          
describeNumber(4)
[1] "whole number"   "even"           "positive"       "perfect square"
describeNumber(0)
[1] "whole number"   "even"           "zero"           "perfect square"
#debugonce(describeNumber)
describeNumber(-7.5)
[1] "not a whole number" "negative"          

34.18 Another version - “Nested” if’s (i.e. an if inside of another if)

# NEW VERSION
#
# The following version uses different forms of the if
#
# - just an if
# - an if with an else
# - an if with an else if and an else
# - "nested" statements, i.e. one if or else or "else if" inside 
#   the body of another one
#
# This makes the code "faster" for R to execute and
# can make writing the code more straight forward for
# people. There is less repetition with this style.


describeNumber <- function(n) {
  # make sure that n is a single number and stop if it isn't
  if (!is.numeric(n) | length(n) != 1){
    stop ("n must be a single number")
  }
  
  answer <- character(0)   # empty answer so far

  # one of the following pieces of code will run , but never both  
  if (trunc(n) == n) {
    answer <- c(answer, "whole number")
    
    if(n%%2 == 0){
      answer <- c(answer, "even")   
    } else {
      answer <- c(answer, "odd")   
    } 
    
    
  } else {
    answer <- c(answer, "not a whole number")
  }
  
  
  # only 1 of the follwing will happen  
  if (n > 0){
    answer <- c(answer, "positive")   
  } else if (n < 0) {
    answer <- c(answer, "negative")   
  } else {
    answer <- c(answer, "zero")
  }
  
  # If you take the sqrt of a negative number R will 
  # give you an error. 
  #
  # Make sure that we don't run the sqrt function unless
  # we know that n is not negative.
  if (n>=0){
    if (sqrt(n) == trunc(sqrt(n))){
      answer <- c(answer, "perfect square")
    }
  }
  
  return (answer)  
}


describeNumber("apple")
Error in describeNumber("apple"): n must be a single number
describeNumber(c(10,20))
Error in describeNumber(c(10, 20)): n must be a single number
describeNumber(2.5)
[1] "not a whole number" "positive"          
describeNumber(7)
[1] "whole number" "odd"          "positive"    
describeNumber(-7.5)
[1] "not a whole number" "negative"          
describeNumber(4)
[1] "whole number"   "even"           "positive"       "perfect square"
describeNumber(0)
[1] "whole number"   "even"           "zero"           "perfect square"
debugonce(describeNumber)
describeNumber(-7.5)
debugging in: describeNumber(-7.5)
debug at <text>#16: {
    if (!is.numeric(n) | length(n) != 1) {
        stop("n must be a single number")
    }
    answer <- character(0)
    if (trunc(n) == n) {
        answer <- c(answer, "whole number")
        if (n%%2 == 0) {
            answer <- c(answer, "even")
        }
        else {
            answer <- c(answer, "odd")
        }
    }
    else {
        answer <- c(answer, "not a whole number")
    }
    if (n > 0) {
        answer <- c(answer, "positive")
    }
    else if (n < 0) {
        answer <- c(answer, "negative")
    }
    else {
        answer <- c(answer, "zero")
    }
    if (n >= 0) {
        if (sqrt(n) == trunc(sqrt(n))) {
            answer <- c(answer, "perfect square")
        }
    }
    return(answer)
}
debug at <text>#18: if (!is.numeric(n) | length(n) != 1) {
    stop("n must be a single number")
}
debug at <text>#22: answer <- character(0)
debug at <text>#25: if (trunc(n) == n) {
    answer <- c(answer, "whole number")
    if (n%%2 == 0) {
        answer <- c(answer, "even")
    }
    else {
        answer <- c(answer, "odd")
    }
} else {
    answer <- c(answer, "not a whole number")
}
debug at <text>#36: answer <- c(answer, "not a whole number")
debug at <text>#41: if (n > 0) {
    answer <- c(answer, "positive")
} else if (n < 0) {
    answer <- c(answer, "negative")
} else {
    answer <- c(answer, "zero")
}
debug at <text>#41: if (n < 0) {
    answer <- c(answer, "negative")
} else {
    answer <- c(answer, "zero")
}
debug at <text>#44: answer <- c(answer, "negative")
debug at <text>#54: if (n >= 0) {
    if (sqrt(n) == trunc(sqrt(n))) {
        answer <- c(answer, "perfect square")
    }
}
debug at <text>#60: return(answer)
exiting from: describeNumber(-7.5)
[1] "not a whole number" "negative"          

34.19 using the debugger to follow which lines of code are actually run

###############################################################################
# You can see exactly how R processes the code by using the debugger to follow
# which lines of code are actually run. Just keep typing "n" or press the "next"
# button  to run the "next" line of code.
###############################################################################

#debugonce(describeNumber)
describeNumber(4)
[1] "whole number"   "even"           "positive"       "perfect square"

34.20 Summary of different forms of if statement

#------------------------------------------------
# Summary of different forms of if statement
#------------------------------------------------

# 1a. if without an else
#
#      if( SOME_TRUE_FALSE_CONDITION ) {
#             LINE 1
#             LINE 2
#             ETC ...
#      }
#
#    either the code happens or it doesn't
#
# 1b. Multiple if's in a row
#
#      if( SOME_TRUE_FALSE_CONDITION ) {
#             LINE 1
#             LINE 2
#             ETC ...
#      }
#
#      if( SOME_TRUE_FALSE_CONDITION ) {
#             LINE 1
#             LINE 2
#             ETC ...
#      }
#
#      if( SOME_TRUE_FALSE_CONDITION ) {
#             LINE 1
#             LINE 2
#             ETC ...
#      }
#
#
#
# 2. if with else
#
#      if( SOME_TRUE_FALSE_CONDITION ) {
#             IF CODE LINE 1
#             IF CODE LINE 2
#             ETC ...
#      } else {
#             ELSE CODE LINE 1
#             ELSE CODE LINE 2
#             ETC ...
#      }
#
#    DEFINITELY one of the blocks of code happens
#    when the condition on the if is TRUE the if happens
#    when the condition on the if is FALSE the else happens
#    they NEVER both happen.
#
# 3. if with one else if (but no else)
#
#      if( SOME_TRUE_FALSE_CONDITION ) {
#             IF CODE LINE 1
#             IF CODE LINE 2
#             ETC ...
#      } else if (ANOTHER_TRUE_FALSE_CONDITION) {
#             ELSE IF CODE LINE 1
#             ELSE IF CODE LINE 2
#             ETC ...
#      }
#
#    Possibly the if happens
#    possibly the else if happens
#    possibly neither of them happen
#    They NEVER both happen
#
# 4. if with more than one else if
#    every if and "else if" has a condition.
#
#      if( SOME_TRUE_FALSE_CONDITION ) {
#             IF CODE LINE 1
#             IF CODE LINE 2
#             ETC ...
#      } else if (ANOTHER_TRUE_FALSE_CONDITION) {
#             ELSE IF CODE LINE 1
#             ELSE IF CODE LINE 2
#             ETC ...
#      } else if (YET_A_THIRD_TRUE_FALSE_CONTISION) {
#             ELSE IF CODE LINE 1
#             ELSE IF CODE LINE 2
#             ETC ...
#      }
#
#    The first 
#    condition that turns out to be TRUE
#    causes the code below that condition to run
#
#    BOTTOM LINE - 
#      o ONE of the if / else if  sections may run
#      o Never more than one section will run
#      o Possibly none of the sections will run
#
# 5. if with else if (possibly many else if's) and a final else
# 
#      if( SOME_TRUE_FALSE_CONDITION ) {
#             IF CODE LINE 1
#             IF CODE LINE 2
#             ETC ...
#      } else if (ANOTHER_TRUE_FALSE_CONDITION) {
#             ELSE IF CODE LINE 1
#             ELSE IF CODE LINE 2
#             ETC ...
#      } else if (YET_A_THIRD_TRUE_FALSE_CONTISION) {
#             ELSE IF CODE LINE 1
#             ELSE IF CODE LINE 2
#             ETC ...
#      } else {
#             ELSE CODE LINE 1
#             ELSE CODE LINE 2
#             ETC ...
#      }
#
#    SAME as above, but when none of the if/"else if" conditions
#    are TRUE then the final else section runs
#
# 6. nested if
#
#    an if 
#    (or if ... else ...
#     or if ... else if ... else ...
#     etc.)
#
#    inside another if
#    (or if ... else ...
#     or if ... else if ... else ...
#     etc.)

34.21 another example

#------------------------------
# another example
#------------------------------

# Write a function to return the price of admission to an amusement park.
#
# Business rules: admission price depends on age and weekday vs weekend
# and age of the guest.

# RULES: 
# WEEKENDS
# adults 13 and up : $40
# children: 4 to 12 : $30
# children below 3 are free
#
# WEEKDAYS
# adults 13 and up : $38
# children: 4 to 12 : $22
# children below 3 are free
#
# ALL TIMES adults 65 and up get 10% off

# day is one of 'mon' 'tue' etc.
# age is a single number

admissionPrice <- function (day, age) {
  # make sure that day is valid - if not then stop with an error
  if( ! (day %in% c("sun","mon","tue","wed","thu","fri","sat"))    ){
    stop( "day must be one of sun mon tue wed thu fri sat")
  }
  
  # make sure that age is valid - if not then stop with an error
  if (!is.numeric(age) | length(age) != 1){
    stop("age must be a single number")
  }
  
  if ( day %in% c("sun","sat")) {

    # THIS IS A WEEKEND
    if(age >= 13){   # ADULT
      price = 40
    } else if (age >= 4){
      price = 30
    } else {
      price = 0
    }

  } else {
    
    # THIS IS A WEEK DAY
    if(age >= 13){   # ADULT
      price = 38
    } else if (age >= 4){
      price = 22
    } else {
      price = 0
    }
  }
  
  # THIS CODE WILL RUN ON BOTH WEEKENDS AND WEEKDAYS
  if (age >= 65) {
    price <- price * .9
  }
  
  return (price)
}


admissionPrice ("sun", 35)   # 40
[1] 40
admissionPrice ("tue", 35)   # 38
[1] 38
admissionPrice ("tue", 75)   # 34.2
[1] 34.2
admissionPrice ("sun", 5)   #
[1] 30
admissionPrice ("sun", 2)   #
[1] 0
admissionPrice ("xyz", 35)   # ERROR: day must be one of sun ...
Error in admissionPrice("xyz", 35): day must be one of sun mon tue wed thu fri sat
admissionPrice ("tue", 35)   # 38
[1] 38

34.22 Some useful functions - sample , readline , set.seed , all , any

############################################################################.
# The main point of this section is to describe the 
# if / else if / else  
# constructs in R. However, the following functions will be useful 
# in constructing examples that demonstrate the concepts
# later.
############################################################################.

responseFromUser = readline( MESSAGE_TO_DISPLAY )

###############################################################################.
# responseFromUser = readline( MESSAGE_TO_DISPLAY )  
#
# The readline function displays a message to the user. Then it takes whatever
# info the user types and returns it as a character value.
#
# NOTE: The code in this section asks the user questions and expects
#       the user to type an answer. Therefore you should copy this code
#       into R to try it. The correct functioning of the code cannot
#       be demonstrated by automatically generating the output since the 
#       output requires the user to type an answer to the questions 
#       that are displayed.
###############################################################################.

################.
# Try this in R
################.

# x = readline("Please enter a number: ")
# x   # This is a character value of whatever was entered by the user

# # to use it as a number you must convert it using as.numeric
# as.numeric(x) * 2   

pickRandomNumbers = function(){
howMany = readline("How many random numbers should we pick? ")
 howMany = as.numeric(howMany)

 largest = readline("What should be the largest possible number? ")
 largest = as.numeric(largest)
 
 sample(largest, howMany)
}

################.
# Try this in R
################.

# randomNumbers = pickRandomNumbers()
# randomNumbers

# randomNumbers = pickRandomNumbers()
# randomNumbers

— Practice —

Using readline to ask a question
#############################################################################.
# Write the following function:
#
#   washingtonsHorse = function()
#
# The function takes no parameters. 
# The function asks the user 
#
# Use the readline() function to ask the question:
#
#   "What color was George Washington's white horse?"
#
# If the user responds with the correct answer (i.e. white) then the function
# should return TRUE, otherwise the function should return FALSE. Assume that
# the correct answer must be entered lowercase. If the answer is in UPPER CASE
# the function should return FALSE.
#
# For example:
#
#    > washingtonsHorse()  # users answer is after the "?" (user get's it wrong)
#    What color was George Washington's white horse? green
#    [1] FALSE
#
#    > washingtonsHorse()  # users answer is after the "?" (user get's it right)
#    What color was George Washington's white horse? white
#    [1] TRUE
#
#    > washingtonsHorse()  # users answer is after the "?" (user get's it wrong, but wrong CASE)
#    What color was George Washington's white horse? WHITE
#    [1] FALSE
#
#############################################################################.
washingtonsHorse = function(){
  answer = readline("What color was George Washington's white horse? ")
  
  if (answer == "white"){
    return(TRUE)
  } else {
    return(FALSE)
  }
}

# NOTE: Since this code involves interaction with the user, the results of 
#       running it cannot be automatically generated for this website. 
#       To try the code copy/paste it into RStudio and run it as follows:
#
#          washingtonsHorse()
Using toupper, tolower for case insensitive responses
#############################################################################.
# Read the documentation for the tolower() function (i.e. ?tolower )
# 
# Use the toupper function to modify the answer to the previous question so 
# that the function returns TRUE as long as the user types "white" in any of 
# the possible cases. For example, the function should return TRUE if the user 
# responds with "white" or "WHITE" or "White" or "wHITe", etc.
#############################################################################.
washingtonsHorse = function(){
  answer = readline("What color was George Washington's white horse? ")
  
  if (tolower(answer) == "white"){
    return(TRUE)
  } else {
    return(FALSE)
  }
}

# NOTE: Since this code involves interaction with the user, the results of 
#       running it cannot be automatically generated for this website. 
#       To try the code copy/paste it into RStudio and run it as follows:
#
#          washingtonsHorse()
Create more questions (nothing new here)
#############################################################################.
# NOTE: This question assumes that you already answered the previous questions
# correctly.
#
# Write 2 more functions that ask different questions (make up your own).
# Your new function should return TRUE if the user gets the right answer
# and FALSE otherwise.
#############################################################################.
# The following is a sample answer - you should make up your own question/answer.

capitalOfNewYork = function(){
  answer = readline("What is the capital of New York state? ")
  
  if (tolower(answer) == "albany"){
    return(TRUE)
  } else {
    return(FALSE)
  }
}

twoPlusTwo = function(){
  answer = readline("What is 2+2 ? ")
  
  if (answer == 4 || tolower(answer) == "four"){
    return(TRUE)
  } else {
    return(FALSE)
  }
}
Create a quiz
#############################################################################.
# NOTE: This question assumes that you already answered the previous questions
# correctly.
#
# Create the following function:
#
#   quiz = function()
#
# The function should make use of the three functions you created for 
# the answers to the previous questions. The function should ask the user
# all three questions. 
#
# The function should display RIGHT or WRONG after each answer the user gives. 
#
# The function should then display a message that includes the number of 
# answers the user got right and wrong (see example below).
# Use "cat" to display this message.
#
# EXAMPLE
#   > quiz()
#   What color was George Washington's white horse? blue
#   WRONG
#
#   What is the capital of New York state? albany
#   RIGHT
#
#   What is 2+2 ? 3
#   WRONG
#
#   You got 1 right and 2 wrong.
#############################################################################.
# The following is a sample answer - you should make up your own question/answer.

quiz = function(){
  totalCorrect = 0
  totalWrong = 0
  
  correct = washingtonsHorse()
  if ( correct ){
    cat("RIGHT\n\n")
    totalCorrect = totalCorrect + 1
  } else {
    cat("RIGHT\n\n")
    totalWrong = totalWrong + 1
  } 

  correct = capitalOfNewYork()
  if ( correct ){
    cat("RIGHT\n\n")
    totalCorrect = totalCorrect + 1
  } else {
    cat("RIGHT\n\n")
    totalWrong = totalWrong + 1
  } 

  correct = twoPlusTwo()
  if ( correct ){
    cat("RIGHT\n\n")
    totalCorrect = totalCorrect + 1
  } else {
    cat("RIGHT\n\n")
    totalWrong = totalWrong + 1
  } 

  # Note - the \n below is required - try taking it out and see what happens.
  cat("\nYou got", totalCorrect, "right and", totalWrong, "wrong.")
}
Better quiz - display the correct answers if user was wrong
#############################################################################.
# NOTE: This question assumes that you already answered the previous questions
# correctly.
#
# Modify the code that you already wrote. In this new version, 
# if the user gets a question wrong the computer should display WRONG 
# followed by the correct answer.
# 
# Keep the info about the questions and answer inside the
# individual question functions. The quiz function should not have any 
# answers directly coded into it. 
# 
# Therefore to do this, modify the code so that 
# all of the messages that are displayed with cat() regarding RIGHT/WRONG a
# specific answer is in the question function.
# The quiz function should only contain the last message as to how many
# the user got right/wrong. 
#
# EXAMPLE
#   > quiz()
#   What color was George Washington's white horse? blue
#   WRONG - the correct answer is "white"
#
#   What is the capital of New York state? albany
#   RIGHT
#
#   What is 2+2 ? 3
#   WRONG - the correct answer is 4
#
#   You got 1 right and 2 wrong.
#############################################################################.
##################################.
# Modify the question functions:
##################################.
washingtonsHorse = function(){
  answer = readline("What color was George Washington's white horse? ")
  correctAnswer = "white"

  if (tolower(answer) == tolower(correctAnswer)){
    cat("RIGHT\n")
    return(TRUE)
  } else {
    cat('WRONG - the correct answer is "', correctAnswer, '"\n', sep="")
    return(FALSE)
  }
}

capitalOfNewYork = function(){
  answer = readline("What is the capital of New York state? ")
  correctAnswer = "Albany"
  
  if (tolower(answer) == tolower(correctAnswer)){
    cat("RIGHT\n")
    return(TRUE)
  } else {
    cat('WRONG - the correct answer is "', correctAnswer, '"\n', sep="")
    return(FALSE)
  }
}

twoPlusTwo = function(){
  answer = readline("What is 2+2 ? ")
  correctAnswer = 4
  
  if (answer == 4 || tolower(answer) == "four"){
    cat("RIGHT")
    return(TRUE)
  } else {
    cat('WRONG - the correct answer is ', correctAnswer, '\n', sep="")
    return(FALSE)
  }
}

##########################################################################.
# Modify the quiz function: 
#
# remove the cat("RIGHT") and cat("WRONG") lines from the quiz function
##########################################################################.
quiz = function(){
  totalCorrect = 0
  totalWrong = 0
  
  correct = washingtonsHorse()
  if ( correct ){
    totalCorrect = totalCorrect + 1
  } else {
    totalWrong = totalWrong + 1
  } 

  correct = capitalOfNewYork()
  if ( correct ){
    totalCorrect = totalCorrect + 1
  } else {
    totalWrong = totalWrong + 1
  } 

  correct = twoPlusTwo()
  if ( correct ){
    totalCorrect = totalCorrect + 1
  } else {
    totalWrong = totalWrong + 1
  } 

  # Note - the \n below is required - try taking it out and see what happens.
  cat("\nYou got", totalCorrect, "right and", totalWrong, "wrong.")
}

sample( SOME_VECTOR , N )

rm(list=ls())

##############################################################################
#
# sample( SOME_VECTOR , SIZE ) 
#
#    randomly select the specified number of values from the vector
##############################################################################

sample(c(10,20,30,40,50,60,70,80,90,100), 5)   # randomly select 5 values
[1]  40  20  90 100  80
sample(c(10,20,30,40,50,60,70,80,90,100), 5)   # randomly select 5 values
[1]  80  70  30  20 100
sample(c(10,20,30,40,50,60,70,80,90,100), 5)   # randomly select 5 values
[1]  70  10 100  90  50
sample(c("pizza", "burgers", "chicken", "soup"), 1)  # randomly select a meal
[1] "pizza"
sample(c("pizza", "burgers", "chicken", "soup"), 1)  # randomly select a meal
[1] "chicken"
sample(c("pizza", "burgers", "chicken", "soup"), 1)  # randomly select a meal
[1] "pizza"
sample(c("pizza", "burgers", "chicken", "soup"), 1)  # randomly select a meal
[1] "pizza"
sample(c("pizza", "burgers", "chicken", "soup"), 1)  # randomly select a meal
[1] "soup"
# replace = true
#
# If you specify replace=TRUE then the same value could be picked more than once
# in the same sample

sample(c(10,20,30,40,50,60,70,80,90,100), 5, replace=TRUE)   # randomly select 5 values
[1]  10  60 100  70  80
sample(c(10,20,30,40,50,60,70,80,90,100), 5, replace=TRUE)   # randomly select 5 values
[1] 20 50 90 40 40
sample(c(10,20,30,40,50,60,70,80,90,100), 5, replace=TRUE)   # randomly select 5 values
[1] 40 90 90 50 60
sample(c(10,20,30,40,50,60,70,80,90,100), 5, replace=TRUE)   # randomly select 5 values
[1] 60 80 90 50 30
sample(c(10,20,30,40,50,60,70,80,90,100), 5, replace=TRUE)   # randomly select 5 values
[1] 90 80 80 40 50

sample(100, SOME_NUMBER)

#--------------------------------------------
# BE CAREFUL: 
#
#     sample(100, SOME_NUMBER) 
#        randomly selects values from 1:100
#--------------------------------------------

# If there is a single positive number as the first argument sample 
# actually chooses from the sequence starting at 1

sample(100, 1)   # a single random number from 1:100
[1] 77
sample(100, 1)   # a single random number from 1:100
[1] 50
sample(100, 1)   # a single random number from 1:100
[1] 33
sample(100, 3)   # 3 random number from 1:100
[1] 60 88  2
# This does the obvious thing ...
sample("burgers", 1)    # burgers (of course - there is only one choice)
[1] "burgers"
sample("burgers", 1)    # burgers (of course - there is only one choice)
[1] "burgers"
sample("burgers", 1)    # burgers (of course - there is only one choice)
[1] "burgers"
# If you sample without specifying a size R will return all of the values
# in a random order

sample(c(10,20,30,40,50,60,70,80,90,100))
 [1]  90  10 100  20  40  80  50  30  60  70
# same as 
sample(c(10,20,30,40,50,60,70,80,90,100), 10)
 [1]  70  40  60  90  80  10  30  20  50 100

“reproducible randomness” - set.seed( SOME_NUMBER )

#------------------------------------------------------------------------------.
# set.seed( SOME_NUMBER )
#
# The sample function is one of several functions that can be used to 
# provide "randomness" in a program (also see ?runif).
#
# If every time you run a program you get different results, it can be hard
# to know for sure if the program is working correctly. It is therefore 
# helpful if there is would be a way to guarantee that the program produces
# the same results every time it is run. This can be done with 
# the set.seed(SOME_NUMBER) function. The number that is passed to the 
# set.seed function is known as the "seed". Each time the set.seed
# function is called with the same seed number, the same sequence of 
# randomness starts again. Different "seed" values cause different
# sequences of randomness. See below for details.
#------------------------------------------------------------------------------.

# set the seed to a specific number (ie. 1) and then choose some random values.
set.seed(1)
sample(c(10,20,30,40,50,60,70,80,90,100), 2, replace=TRUE)   # randomly select 2 values
[1] 90 40
sample(c(10,20,30,40,50,60,70,80,90,100), 2, replace=TRUE)   # randomly select 2 values
[1] 70 10
sample(c(10,20,30,40,50,60,70,80,90,100), 2, replace=TRUE)   # randomly select 2 values
[1] 20 70
# set the seed to a different specific number (ie. 2) to get different random values
set.seed(2)
sample(c(10,20,30,40,50,60,70,80,90,100), 2, replace=TRUE)   # randomly select 2 values
[1] 50 60
sample(c(10,20,30,40,50,60,70,80,90,100), 2, replace=TRUE)   # randomly select 2 values
[1] 60 80
sample(c(10,20,30,40,50,60,70,80,90,100), 2, replace=TRUE)   # randomly select 2 values
[1] 10 10
# If we set the seed back to 1 again, we will get the same random values
# that we got the first time we set the seed to 1.
set.seed(1)
sample(c(10,20,30,40,50,60,70,80,90,100), 2, replace=TRUE)   # randomly select 5 values
[1] 90 40
sample(c(10,20,30,40,50,60,70,80,90,100), 2, replace=TRUE)   # randomly select 5 values
[1] 70 10
sample(c(10,20,30,40,50,60,70,80,90,100), 2, replace=TRUE)   # randomly select 5 values
[1] 20 70
# another example

set.seed(1567)   # the seed can be any number
sample(1:100, 1)
[1] 27
sample(1:100, 1)
[1] 7
sample(1:100, 1)
[1] 50
sample(1:100, 1)
[1] 89
set.seed(1567)   # set the seed again to the same number to get the same results
sample(1:100, 1)
[1] 27
sample(1:100, 1)
[1] 7
sample(1:100, 1)
[1] 50
sample(1:100, 1)
[1] 89

—Practice—

Create a quiz
#############################################################################.
# Write the following function:
# 
#   additionQuestion = function()
#
# The function should ask the user to add two single digit positive numbers. 
# (see examples below). 
#
# The function should return TRUE if the user gives the correct answer
# and FALSE otherwise.
# 
# EXAMPLES
#
#    > additionQuestion()
#      What is 2+5 ? 8
#############################################################################.
# The following is a sample answer - you should make up your own question/answer.

quiz = function(){
  totalCorrect = 0
  totalWrong = 0
  
  correct = washingtonsHorse()
  if ( correct ){
    cat("RIGHT\n\n")
    totalCorrect = totalCorrect + 1
  } else {
    cat("RIGHT\n\n")
    totalWrong = totalWrong + 1
  } 

  correct = capitalOfNewYork()
  if ( correct ){
    cat("RIGHT\n\n")
    totalCorrect = totalCorrect + 1
  } else {
    cat("RIGHT\n\n")
    totalWrong = totalWrong + 1
  } 

  correct = twoPlusTwo()
  if ( correct ){
    cat("RIGHT\n\n")
    totalCorrect = totalCorrect + 1
  } else {
    cat("RIGHT\n\n")
    totalWrong = totalWrong + 1
  } 

  # Note - the \n below is required - try taking it out and see what happens.
  cat("\nYou got", totalCorrect, "right and", totalWrong, "wrong.")
}

all(SOME_LOGICAL_VECTOR)     any(SOME_LOGICAL_VECTOR)

The all function can be used in lieu of multiple && operators.

The any function can be used in lieu of multiple || operators.

See below for details.

#######################################################
#
# all(SOME_LOGICAL_VECTOR)
#
#    returns TRUE if ALL of the logical values are TRUE
#    returns FALSE otherwise
#
#
# any(SOME_LOGICAL_VECTOR)
#
#    returns TRUE if ANY of the logical values are TRUE
#    returns FALSE otherwise
#######################################################

all(c(TRUE,TRUE,TRUE,TRUE))  # TRUE
[1] TRUE
all(c(TRUE,TRUE,FALSE,TRUE))  # FALSE
[1] FALSE
any(c(FALSE,TRUE,FALSE,FALSE,FALSE))  # TRUE
[1] TRUE
any(c(FALSE,FALSE,FALSE,FALSE,FALSE))  # FALSE
[1] FALSE
x = c(10, 5,  300)
y = c(5,  20, 145)
all(x>y) # FALSE
[1] FALSE
x = c(10, 5000,  300)
y = c(5,  20,    145)
all(x>y) # TRUE
[1] TRUE
x = c(1,  2,  3)
y = c(100,200,300)
any(x > y) # FALSE
[1] FALSE
x = c(1,  999,  3)
y = c(100,200,300)
any(x > y) # TRUE
[1] TRUE