#################################################
#################################################
##
## 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
= function( num ){
commentsAboutNumber 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.
#-----------------------------------------------------------------
= function( num ){
commentsAboutNumber
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}
= function( num ){
commentsAboutNumber
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
#..................................................................
= function(size) {
priceOfSalad
# 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") {
= 9
price
}
if (size == "medium"){
= 12
price
}
if (size == "large") {
= 14
price
}
if (size == "xlarge") {
= 15
price
}
# return the total price
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.
= function(size) {
priceOfSalad
# 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") {
= 9
price
}
if (size == "medium"){
= 12
price
}
if (size == "large") {
= 14
price
}
if (size == "xlarge") { # an else DOES NOT HAVE a condition
= 15
price
}
# return the total price
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.
= function(num1, num2){
addSquareRoots 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
= function(num1, num2){
addSquareRoots 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
= function(num1, num2){
addSquareRoots
# 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
#---------------------------------------------------------
= addSquareRoots(49, -4) # stop with an ERROR answer
Error in addSquareRoots(49, -4): you specified a negative number. num1 and num2 may not be negative
# ERROR - answer has no value answer
Error in eval(expr, envir, enclos): object 'answer' not found
= addSquareRoots(49, 4) # This time it works
answer # 9 answer
[1] 9
= addSquareRoots(-1 , -1) # stop with an ERROR answer
Error in addSquareRoots(-1, -1): you specified a negative number. num1 and num2 may not be negative
# 9 - it never changed since the previous line ended with an error answer
[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.
= function(size) {
priceOfSalad
# EXACTLY ONE AND ONLY ONE of the following sections will execute.
if (size == "small") {
= 9
price
else if (size == "medium") {
} = 12
price
else if (size == "large") {
} = 14
price
else if (size == "xlarge") {
} = 15
price
else { # if nothing else happened, this happens. An "else" does NOT have a condition
} stop("size must be one of : small , medium, large, xlarge")
}
# return the total price
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
= function(size) {
priceOfSalad
# EXACTLY ONE AND ONLY ONE of the following sections will execute.
if (size == "small") {
= 9
price
else if (size == "medium") {
} = 12
price
else if (size == "large") {
} = 14
price
else if (size == "xlarge") {
} = 15
price
}
# return the total price
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
= function(size) {
priceOfSalad
# if you don't specify a correct size, the price will be this:
= 0
price
# if the size were not small, medium, large or xlarge
# we would not have gotten this far.
if (size == "small") {
= 9
price
else if (size == "medium") {
} = 12
price
else if (size == "large") {
} = 14
price
else if (size == "xlarge") {
} = 15
price
}
# 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)
= function(age){
admissionPrice
# EXACTLY ONE OF THE PRICES WILL BE ASSIGNED
if (age >= 65)
= 30
price
else if (age >= 13)
= 40
price
else if (age >= 4)
= 25
price
else # DO NOT USE ANY CONDITION WITH A FINAL ELSE
= 0
price
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.
= function(age){
admissionPrice
if (age >= 65) {
= 30
price
}
if (age >= 13) {
= 40
price
}
if (age >= 4) {
= 25
price
}
# THIS WILL ALWAYS HAPPEN - FOR ANY AGE !!!
if (age >= 0) {
= 0
price
}
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. ***
= function(age){
admissionPrice
= 0
price
if (age >= 4)
= 25 # this WILL happen if the age is 70
price
if (age >= 13)
= 40 # this will ALSO happen if the age is 70
price
if (age >= 65)
= 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
price
}
# 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. ***
= function(age){
admissionPrice
= 0
price
if (age >= 4 & age < 13) # code is overly complex and error prone
= 25
price
if (age >= 13 & age < 65) # code is overly complex and error prone
= 40
price
if (age >= 65)
= 30
price
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. ***
= function(age){
admissionPrice
= 0
price
if (age >= 4 & age < 13){ # this is NOT connected with the next if
= 25
price
}
if (age >= 13 & age < 65){ # this IS connected to the following else
= 40
price
else { # THIS LOGIC IS WRONG - this will happen for 5-year-olds
} = 30
price
}
# correct for adults and seniors WRONG for children and infants
price
}
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
= function(size, dressing) {
priceOfSalad
# 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)"
= 0.50
price else { # an else DOES NOT HAVE a condition
} = 0
price
}
# STARTING A NEW "if"
#
# EXACTLY ONE OF THE FOLLOWING BLOCKS OF CODE HAPPENS
if (size == "small") {
= price + 9
price
else if (size == "medium") {
} = price + 12
price
else if (size == "large") {
} = price + 14
price
else { # an else DOES NOT HAVE a condition
} = price + 15
price
}
# return the total price
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)
<- function(n) {
describeNumber
# 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
<- character(0) # empty answer so far
answer
# 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) {
<- c(answer, "whole number")
answer
}
if (trunc(n) != n) {
<- c(answer, "not a whole number")
answer
}
if(n%%2 == 0){
<- c(answer, "even")
answer
}
if(n%%2 == 1){
<- c(answer, "odd")
answer
}
if (n > 0){
<- c(answer, "positive")
answer
}
if (n < 0) {
<- c(answer, "negative")
answer
}
if (n==0) {
<- c(answer, "zero")
answer
}
# 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
<- c(answer, "perfect square")
answer
}
}
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.
<- function(n) {
describeNumber # 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")
}
<- character(0) # empty answer so far
answer
# one of the following pieces of code will run , but never both
if (trunc(n) == n) {
<- c(answer, "whole number")
answer
if(n%%2 == 0){
<- c(answer, "even")
answer else {
} <- c(answer, "odd")
answer
}
else {
} <- c(answer, "not a whole number")
answer
}
# only 1 of the follwing will happen
if (n > 0){
<- c(answer, "positive")
answer else if (n < 0) {
} <- c(answer, "negative")
answer else {
} <- c(answer, "zero")
answer
}
# 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))){
<- c(answer, "perfect square")
answer
}
}
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
<- function (day, age) {
admissionPrice # 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
= 40
price else if (age >= 4){
} = 30
price else {
} = 0
price
}
else {
}
# THIS IS A WEEK DAY
if(age >= 13){ # ADULT
= 38
price else if (age >= 4){
} = 22
price else {
} = 0
price
}
}
# THIS CODE WILL RUN ON BOTH WEEKENDS AND WEEKDAYS
if (age >= 65) {
<- price * .9
price
}
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
= function(){
pickRandomNumbers = readline("How many random numbers should we pick? ")
howMany = as.numeric(howMany)
howMany
= readline("What should be the largest possible number? ")
largest = as.numeric(largest)
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
= c(10, 5, 300)
x = c(5, 20, 145)
y all(x>y) # FALSE
[1] FALSE
= c(10, 5000, 300)
x = c(5, 20, 145)
y all(x>y) # TRUE
[1] TRUE
= c(1, 2, 3)
x = c(100,200,300)
y any(x > y) # FALSE
[1] FALSE
= c(1, 999, 3)
x = c(100,200,300)
y any(x > y) # TRUE
[1] TRUE