#################################################
#################################################
##
## Various forms of if statement
##
#################################################
#################################################34 32. “if / else if / else” constructs
34.1 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 assignedError 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, xlargeError 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 ERRORError 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 ERRORError in addSquareRoots(49, -4): you specified a negative number. num1 and num2 may not be negative
answer # ERROR - answer has no valueError 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 ERRORError 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 HERE34.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 foundError 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 HERE34.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 —
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—
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