factorial(3) # same as 3*2*1 which is 6[1] 6
factorial(5) # same as 5*4*3*2*1 which is 120[1] 120
A “while loop” in R is one of several ways to get R to do something over and over again (we’ll discuss other ways later). A relatively straight forward example of code that could be written with a “while loop” is the factorial function (see below). Now, since R has a built-in version of the factorial function, you don’t actually have to write the factorial function yourself. However, it will be instructive to reproduce this function with our own code that uses a while loop - which we do below. In order to understand the example code below, let’s first understand what a factorial is.
In math, the factorial of a number is the product of that number and all the positive whole numbers below it. For example:
The factorial of 5 is: 5 × 4 × 3 × 2 × 1 = 120
Mathematicians write factorials using an exclamation point.
For example a mathematician would write 5! to mean 5 × 4 × 3 × 2 × 1 = 120
However, 5! is NOT valid R code. Rather, R has a builtin function, factorial(SOME_NUMBER) that calculates factorials. For example:
The concept of a factorial is not defined for negative numbers. When given a negative number, R’s factorial function returns NaN (i.e. R’s special value meaning “not a number”)
The factorial of 0 and the factorial of 1 are both defined to be 1.
[1] 1
[1] 1
To learn more about factorials, see this page: https://www.mathsisfun.com/numbers/factorial.html
Let’s write a function called myFactorial that reproduces the results of R’s builtin factorial function that was shown above.
The code introduces a new concept - the “while loop”. We’ll explain all of the code below, but first let’s see the full definition of the function and make sure that it works:
Some examples of using the myFactorial function:
[1] 1
[1] 1
[1] 6
[1] 120
[1] 3628800
myFactorial(100) # 9.332622e+157 - this is a VERY VERY large number (see section on "scientific notation")[1] 9.332622e+157
Error in !is.numeric(num) || trunc(num) != num: 'length = 4' in coercion to 'logical(1)'
[1] NaN
Let’s go through the myFactorial function in detail. Portions of the code are highlighted with numbers and explained below.
myFactorial <- function(num){
1 if(!is.numeric(num) || trunc(num) != num || length(num) != 1) {
stop("num must be a single positive whole number")
}
2 if (num < 0) {
return(NaN)
}
3 answer = 1
4 while(num > 1) {
answer <- answer * num
num <- num - 1
}
5 return(answer)
}A while loop looks similar to an if, except that a while loop starts with the word “while” and an if starts with the word “if”.
Similar to an “if”, a “while” has a logical condition in parentheses followed by some code in {curly braces}. (The code in the {curly braces} is called the “body” of the while loop.)
Similar to an “if”, the condition must evaluate to TRUE or FALSE.
Similar to an “if” - when the condition for the while is TRUE - the code in the {curly braces} is executed and when the condition is FALSE, the code in the {curly braces} is NOT executed.
What makes a while different from an if is the following:
o if : when the code in the {curly braces} (i.e. the “body” of the if) finishes, the execution of the code continues with the first line after the body of the “if”
o while : when the code in the {curly braces} (i.e. the “body” of the while) finishes, the entire “while” is repeated - i.e. the condition is checked and if it is still TRUE - the body of the while is done again. This keeps happening as long as the condition is TRUE (i.e. “while” the condition is TRUE). If/when the condition eventually becomes FALSE, the execution of the code continues with the first line after the body of the “while”.
As a while loop executes, the code in the “body” of the while loop may be executed (i.e. run) several times. Each time the code of the body is executed is known as a single “iteration” of the while loop. In general we say that the while loop “iterates over” the code in the body of the loop.
It is often helpful to use R’s debugger to help understand the code for a loop. Information about how to use R’s debugger is above in the debugger section.
As a quick refresher - to use the debugger, you should first run the code shown above that defines the function myFactorial. Then type the command debugonce(myFactorial) and finally call the function, e.g. myFactorial(5). At that point the debugger should start. To see how the code works, you can repeatedly press the “next” button (or type n). Every time you do so, the code will advance one line. You can use this technique to see what “path” the code takes through ifs and loops. You should pay attention to how the values of the variables in the “Environment” pane change as the code is executed. Tracing through code in this way is one of the best ways to understand how the code works.
When writing while loops, keep the following in mind:
The condition, which appears in the parentheses, should depend on at least one variable.
In this example, the condition is (num > 1). This particular condition contains one variable, i.e. num. It is very possible for a condition to be more complicated than this and contain more than one variable.
The condition must be true for the code in the body to be executed. The condition must be false for the code in the body to NOT be executed.
The value of the variable(s) in the condition must be setup in the code that appears BEFORE the while.
In this example, the value of num was passed into the function, therefore its value was set before the while loop started.
The code inside the {curly braces} is known as the “body” of the loop.
The code in the body should eventually cause the condition to become false. The most common way to do that is for the body to change the value of a variable in the condition in some way.
In this example, the line : num <- num - 1 changes the value of num by subtracting one from it. If this happens enough times, the value of num will eventually become 1, thus making the condition FALSE.
#########################################################################
#
# *** IMPORTANT ***
#
# Writing code with loops can be tricky, especially if you're new at it.
# Watch out for potential coding errors ... be CAREFUL!!!
#########################################################################
#....................................................................
# Infinite loops
#....................................................................
# IMPORTANT ... When writing code with while loops it is possible to
# introduce errors in which the loop will "never end".
# This is called an "infinite loop". If your code enters an "infinite loop",
# RStudio will become unresponsive. If you don't know what to do it can be
# very frustrating!! When this happens, either:
#
# - a little red "stop sign" button usually appears above the console
# window pane in RStudio. Pressing the "stop sign" will stop the function
# from running and let you once again use RStudio normally.
#
# - If you don't see the stop sign, try pressing the ESC key. This can
# happen if you while loop is running with a call to readline() or a
# similar function inside the loop.
#
# In the following code, I purposely introduced an "infinite loop". You
# will not be able to move on until you press the "stop sign" button that
# is above the console window pane in RStudio.
#....................................................................
# The following version of myFactorial has a bug that
# causes an infinite loop.
badMyFactorial <- function(num){
if (num <0 || !is.numeric(num) || trunc(num) != num || length(num) != 1) {
stop("num must be a single positive whole number")
}
answer = 1
# A while loop is similar to an if in that if the condition is true
# then the code in the body runs. If the condition is false the body does not run
# and the next line of the program after the body runs.
# For every while loop you must keep in mind the following
# 1. The condition must depend on SOME variable
# 2. The body must eventually cause the condition to become false.
# The most common way to do that is for the body to change
# the value of a variable in the condition in some way.
while(num > 1) {
answer <- answer * num
# num <- num - 1 (I purposely "commented out" this line to cause an infinite loop)
}
return(answer)
}
# The following calls actually work correctly.
# This is becuase the while loop never even starts since the condition, (num > 1),
# is FALSE even before the first "iteration" of the while loop.
badMyFactorial(0)[1] 1
[1] 1
# The following calls to badMyFactorial cause an infinite loop.
# badMyFactorial(2)
# badMyFactorial(5)
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# inifinite loop - press the "stop sign button" (above the console window pane)
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
badFunction = function (num){
# This is an infinite loop
while (TRUE) {
cat ('You are in an "infinite loop".\n')
cat('Press the "stop sign" button (above the console window pane) to ',
'stop the infinite loop\n\n')
Sys.sleep(0.9) # this causes R to "go to sleep" for 0.9 seconds
}
}
# badFunction(1) # This will result in an "infinite loop". Press the "stop button".
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Another inifinite loop - there is no stop sign button.
#
# If you enter an infinite loop while the computer is waiting for the user
# to type something, you will NOT see a "stop sign button". Instead to
# get out of the loop
#
# - click on the console window pane
# - then click on the ESC key
# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
anotherBadFunction = function() {
# This is an infinite loop. You will NOT see a stop sign button.
# Instead press ESC to get out of the loop.
while(TRUE){
x = readline("What's your name? (to stop looping, click console window, then press ESC)")
}
}
# The following will cause another infinite loop
# Click console window then press ESC key to stop the loop and get back the prompt.
#
#
# anotherBadFunction() # another infinite loop -# The following is the same exact code as above, without all the
# comments. There are many different subtle errors that can pop up
# when writing while loops. Think about what would happen in each of the
# following situations ..
#
# What would happen in each of the situations listed below?
# To find out, change the code and try it.
#
# For each of the following questions, try to figure out what will happen
# before actually running the code. Then change the code and run it.
# To help you figure out what will happen, keep track of the values of all
# the variables and arguments on a piece of paper.
# Then, every time you "run a line of code in your head" keep track of any
# changes to the variables on the piece of paper.
#
# You can also use the debugger ...
#
# What would happen if ...
# 1. ... instead of "answer=1" the programmer typed "answer=0" ?
# 2. ... instead of "while(num>1)" the programmer typed "while(num<1)" ?
# 3. ... instead of "num<-num+1" the programmer typed "num<-num-1"
# 4. ... the programmer forgot to type the line "num<-num-1" and just left it out?
# 5. ... the programmer wrote the lines "answer<-answer*num" and "num<-num-1"
# in the opposite order, i.e. "num<-num-1" and then "answer<-answer*num"
myFactorial <- function(num){
if (num <0 || !is.numeric(num) || trunc(num) != num || length(num) != 1) {
stop("num must be a single positive whole number")
}
answer = 1
while(num > 1) {
answer <- answer * num
num <- num - 1
}
return(answer)
}
myFactorial(4)[1] 24
[1] 24
[1] 9.332622e+157
#--------------------------------------
# Another example ...
#--------------------------------------
# The following function
# returns TRUE if num is a prime number and
# returns FALSE is num is not a prime number
#
# A prime number is a whole number (2 or more) that is
# divisible by only 1 and itself.
#
# Technically 1 is NOT a prime number
# https://blogs.scientificamerican.com/roots-of-unity/why-isnt-1-a-prime-number/
is.prime <- function( num ) {
if (num < 2){
return(FALSE)
}
divisor <- 2
# if at any point you find that num is divided
# evenly by some divisor, return FALSE
while ( divisor < num ) {
if (num %% divisor == 0){
return(FALSE)
}
divisor <- divisor + 1
}
return(TRUE)
}
is.prime(7) # TRUE[1] TRUE
[1] FALSE
[1] TRUE
[1] FALSE
#----------------------------------------------------
# Making the code "more efficient"
#----------------------------------------------------
# When the number gets large the while loop will need to "loop" many times.
# This can take some time even on a computer.
is.prime(181) # TRUE[1] TRUE
#is.prime(15485867) # TRUE (takes a few seconds to run)
#is.prime(236887699) # TRUE (takes some time to run) - press "stop button" to cancel
is.prime(236887695) # FALSE - very fast ... why?[1] FALSE
#---------------------------------------------------------------------------
# Making a program more "efficient"
#---------------------------------------------------------------------------
# Do you really need to check all of the divisors from 2 through num-1 ?
#
# Obvious improvements:
# - if a num is even you know that result is FALSE
# - if num ends in 5 or 0 you know it is divisible by 5 so result is FALSE
#
# Non-obvious improvment:
# - you only need to check the divisors from 2 through the sqrt(num) ... not through num
# This speeds up the code A LOT.
#
# "Computer science" classes focus a lot on how to improve the "efficiency" of
# programs. We will NOT focus on efficiency. However, you should be familiar
# with the general issue.
#---------------------------------------------------------------------------
# With the knowledge of the above "non-obvious improvement"
# let's write a "more efficient" version of the function.
# The following version also works but is "faster", i.e. it doesn't need to
# check as many numbers.
#
# The code is the same as the previous version except for the
# line below that says "#This line changed"
better.is.prime <- function( num ) {
if (num < 2){
return(FALSE)
}
divisor <- 2
while ( divisor <= sqrt(num) ) { # This line changed
if (num %% divisor == 0){
return(FALSE)
}
divisor <- divisor + 1
}
return(TRUE)
}
better.is.prime(181) # TRUE[1] TRUE
[1] TRUE
[1] TRUE
# The following is the same function again, this time without the comments.
#
# For each of the following questions, try to figure out what will happen
# before actually running the code. Then change the code and run it.
# To help you figure out what will happen, keep track of the values of all the variables
# and arguments on a piece of paper. Every time you "run a line of code in
# your head" keep track of any changes to the variables on the piece
# of paper. You can also use the debugger ...
#
# What would happen if ...
# 1. ... instead of "divisor <- 2" the programmer typed "divisor <- 1" ?
# 2. ... instead of "while(divisor<num)" the programmer typed "while(divisor>num)" ?
# 3. ... instead of "while(divisor<num)" the programmer typed "while(divisor<=num)" ?
# 4. ... the line "divisor<-divisor+1" was mistakenly left out?
# 5. ... the line "divisor<-divisor+1" was inside the body of the if?
# 6. ... the line "divisor<-divisor+1" was before the if instead of after the if?
# 7. ... the line "divisor<-divisor+1" was before the if instead of after the if ...
# and instead of "divisor <- 2" the programmer typed "divisor <- 1" ?
# 8. ... the programmer forgot to type the last line "return(TRUE)".
# 9. ... instead of "while(divisor<num)" the programmer typed "while(divisor<num/2)" ?
# 10. ... instead of "while(divisor<num)" the programmer typed "while(divisor<sqrt(num))" ?
is.prime <- function( num ) {
if (num < 2){
return(FALSE)
}
divisor <- 2
while ( divisor < num ) {
if (num %% divisor == 0){
return(FALSE)
}
divisor <- divisor + 1
}
return(TRUE)
}
is.prime(35)[1] FALSE
[1] TRUE
#----------------------------------------------------
# Another way to write the same function.
#
# This version has a single return statement at the end of the function.
# Some people argue that this style is "cleaner" and
# easier to understand when reading the code.
#----------------------------------------------------
is.prime2 <- function( num ) {
answer <- TRUE # assume answer is TRUE unless we find out otherwise
if (num < 2){
answer <- FALSE
} else {
divisor <- 2
while ( divisor < num ) {
if (num %% divisor == 0){
answer <- FALSE
}
divisor <- divisor + 1
}
}
return(answer)
}
is.prime2(35) # FALSE[1] FALSE
[1] TRUE
#--------------------------------------------------
# Write a function to find all divisors of a number
# (assume that num is a positive whole number)
#--------------------------------------------------
divisors <- function(num){
if (!is.numeric(num) || trunc(num)!=num || num<1 || length(num)!=1){
stop("num must be a single positive whole number")
}
# This is the variable we will return at the end of the function.
answer <- 1 # 1 is a divisor of all positive whole numbers
divisor <- 2
while(divisor <= num){
if (num %% divisor == 0){
answer <- c(answer, divisor) # add another number to the answer
}
divisor <- divisor + 1
}
return(answer)
}
#debugonce(divisors)
divisors(12)[1] 1 2 3 4 6 12
[1] 1 3 5 15
[1] 1 2 3 4 6 9 12 18 36
[1] 1 2 4 5 10 20 25 50 100
[1] 1 101
#-------------------------------------
# a more efficient version
#-------------------------------------
divisors.faster <- function(num){
if (!is.numeric(num) || trunc(num)!=num || num<1 || length(num)!=1){
stop("num must be a single positive whole number")
}
answer <- c(1,num) # changed this line
divisor <- 2
while(divisor <= sqrt(num)){ # changed this line (why?)
if (num %% divisor == 0){
answer <- c(answer, divisor)
answer <- c(answer, num/divisor) # added this line (why?)
}
divisor <- divisor + 1
}
answer <- sort(unique(answer)) # added this line (why?)
return(answer)
}
#debugonce(divisors.faster)
divisors.faster(36)[1] 1 2 3 4 6 9 12 18 36
[1] 1 2 3 4 6 12
[1] 1 2 5 10
[1] 1 97
[1] 1 2 4 5 10 20 25 50 100
[1] 1 15485863
[1] 1 2 4 8 31 41 62 82
[9] 124 164 248 328 1271 1523 2542 3046
[17] 5084 6092 10168 12184 47213 62443 94426 124886
[25] 188852 249772 377704 499544 1935733 3871466 7742932 15485864
[1] 1 67867979
[1] 1 2 5 10 6786797 13573594 33933985 67867970
###############################################################################
# The next two "QUESTIONS" (i.e. to write the "mysum" and "blastoff" functions)
# do not present any new concepts. They are just additional examples.
###############################################################################
#----------------------------------------------------------
# QUESTION
# Write a function that simulates the sum function
#
# mysum = function( nums )
#
# nums is expected to be a numeric vector
# mysum should return the sum of all the numbers in nums
# DO NOT USE THE SUM FUNCTION
#----------------------------------------------------------
mysum = function( nums ){
theSum = nums[1]
position = 2
while(position <= length(nums)){
theSum = theSum + nums[position]
position = position + 1
}
theSum # return the answer
}
mysum(c(10,20,5)) # 35[1] 35
[1] 10
# Note that the following returns NA, which makes sense because a sum
# is not applicable (i.e. Not Available) if there are no numbers specified.
mysum( numeric(0) ) [1] NA
[1] NA
#-------------------------------------------------------------------
# The following is a SLIGHTLY different version of the function.
# In this version we returned a sum of 0 when the numeric vector
# is an empty vector. This is also a reasonable answer.
#-------------------------------------------------------------------
mysum = function( nums ){
theSum = 0 # this line changed
position = 1 # this line changed
while(position <= length(nums)){
theSum = theSum + nums[position]
position = position + 1
}
theSum # return the answer
}
mysum(c(10,20,5)) # 35[1] 35
[1] 0
#---------------------------------------------------------
# QUESTION
#
# Write a function
#
# countdown = function(from)
#
# that counts down as shown below. There should be 1 second
# pause between each line of output HINT: use Sys.sleep(1)
#
# > countdown(5)
# T minus 5 seconds
# T minus 4 seconds
# T minus 3 seconds
# T minus 2 seconds
# T minus 1 second
# BLASTOFF!!!
#
# > countdown(3)
# T minus 3 seconds
# T minus 2 seconds
# T minus 1 second
# BLASTOFF!!!
#
# > countdown(0)
# BLASTOFF!!!
#-----------------------------------------------------------
###########.
# ANSWER
###########.
countdown = function(from){
while(from > 0){
if(from == 1){
cat("T minus", from, "second\n")
} else {
cat("T minus", from, "seconds\n")
}
Sys.sleep(1)
from = from - 1
}
cat("BLASTOFF!!!")
}
countdown(5)T minus 5 seconds
T minus 4 seconds
T minus 3 seconds
T minus 2 seconds
T minus 1 second
BLASTOFF!!!
####################################################################
####################################################################
##
## Generating random numbers
##
## - runif : returns a random number
## - set.seed(SOME_WHOLE_NUMBER) : resets the random number generator
## - sample
##
####################################################################
####################################################################
# There are several different functions that are built into R for
# generating "random numbers". These are useful for "simulations"
# e.g. to generate random
# View the help pages by typing:
#
# ?runif
# ?set.seed
# ?sample
#--------------------------------------------------------------
#
# runif
#
# runif stands for "Random number from a UNIForm distribution
#
#--------------------------------------------------------------
runif(1) # one random number between 0 and 1 (not including 0.0 or 1.0)[1] 0.4403264
[1] 0.7603103
[1] 0.5786796 0.1536499 0.2840610
[1] 52.16045 20.15071 32.86659
[1] 503.6439 502.6814 504.9066
[1] 3 1 8 5 9 3 3 6 10 8 3 6 10 3 9 5 2 9 1 9 7 9 9 3 4
[1] 3 10 3 9 5 9 2 7 5 7 2 2 6 1 7 9 9 8 3 5 8 8 7 3 1
#-----------------------------
# set.seed(SOME_WHOLE_NUMBER)
#-----------------------------
set.seed(1)
trunc(runif(3, min=1, max=10)) # 5 random whole numbers between 1 and 9[1] 3 4 6
[1] 9 2 9
[1] 9 6 6
[1] 3 4 6
[1] 3 4 6
[1] 9 2 9
[1] 9 6 6
[1] 6 2 7
[1] 9 5 9
[1] 6 2 7
[1] 9 5 9
set.seed(1) # back to first sequence of numbers
trunc(runif(3, min=1, max=10)) # start again with same numbers[1] 3 4 6
[1] 9 2 9
#-----------------------------------------------------------------------------
# sample
#
# NOTE: This is review. We already covered the sample function in an earlier class.
#-----------------------------------------------------------------------------
# View the help page by typing:
#
# ?sample
sample(c(10,20,30,40,50,60,70,80,90,100), 3) # sample 3 items from the set[1] 70 20 30
[1] 30 10 50 80 20 60 100
[1] 90 50 100 10 70 80 60 20 30 40
[1] 90 10 40 30 60 20 50 80 100 70
# with replacement
sample(c(10,20,30,40,50,60,70,80,90,100), 7, replace=TRUE) # allow same item more than once[1] 40 100 90 70 60 90 80
[1] 90 70 80 60 100 70 30 100 60 80 20 20 60 60 10 30 30 80 60
[20] 70 60 80 70 10 40
Error in sample.int(length(x), size, replace, prob): cannot take a sample larger than the population when 'replace = FALSE'
Error in sample.int(length(x), size, replace, prob): cannot take a sample larger than the population when 'replace = FALSE'
[1] 8 9 10 1 6 4 3 7 5 2
[1] 6 1 5 6 1 9 7 7 3 6
# set probabilities on specific values
sample(c(1,2,3), 25, replace=TRUE, prob=c(.7,.2,.1)) # 70% prob 1, 20% prob 2, 10% prob 3 [1] 1 1 2 1 1 1 3 1 1 1 2 1 1 1 1 1 1 1 1 1 3 1 1 1 3
[1] 1 1 1 1 1 2 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1 2
# set.seed works for sample too
set.seed(9876)
sample(c(1,2,3), 25, replace=TRUE, prob=c(.7,.2,.1)) # 70% prob 1, 20% prob 2, 10% prob 3 [1] 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 1 1 1 3 2 1 1 1
[1] 1 1 2 1 1 1 1 3 1 1 3 3 1 1 2 2 1 2 1 1 2 1 1 1 1
set.seed(9876)
sample(c(1,2,3), 25, replace=TRUE, prob=c(.7,.2,.1)) # 70% prob 1, 20% prob 2, 10% prob 3 [1] 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 1 1 1 3 2 1 1 1
[1] 1 1 2 1 1 1 1 3 1 1 3 3 1 1 2 2 1 2 1 1 2 1 1 1 1
#-------------------------------------------------------------------------
# Write a guessing game function
#-------------------------------------------------------------------------
# Function should
# 1. pick a random number between 1 and 100
# 2. allow the user to guess the number
# 3. keep looping until the user guesses correctly
# 4. return the number of times it took the user to guess correctly
#-------------------------------------------------------------------------
# NOTE: The following line of code that appears in the function is important
# but easy to forget to include. This line of code converts the guess
# from character to numeric. Without this line you would wind up with a
# very hard to catch bug.
#
# guess <- as.numeric(guess)
#
# Remember that readline always returns a character value.
# Without the code shown above, guess would be character and num would be numeric.
# This would have caused the following problem:
#
# 1. The code "if(guess < num)", which appears in the code below will
# be comparing a character value with a numeric value.
#
# 2. Remember that character values sort and compare differently than
# numeric values. For the purpose of this dicussions, remember
# that "29" and "3" are both character values, while
# 29 and 3 (without the quotes) are numeric values.
#
# 29 < 3 is obviously FALSE, however ..
#
# "29" < "3" is TRUE!!!
#
# This is because character values have different rules for
# comparison than numeric values do. (Remember - because the first
# character, in "29" i.e. the "2" is less than the first charcter
# in "3", i.e. "3", "29" is less than "3".
#
# 3. Because the code "guess < num" compares a character value (i.e. guess)
# and a numeric value (i.e. guess) the rules of "implicit conversions"
# determines that both values will be implicitly converted
# to character values. That means that if guess was "29" and num was 3
# "29" < 3 would be implicitly converted to "29" < "3" which would
# be TRUE! That would cause the block of code following
# if(guess < num) be executed and the user would be told
# "higher, guess again:" instead of the correct answer of
# "lower, guess again".
#
# 4. By converting the guess to numeric, the code if(guess < num) will
# now correctly compare two numeric values and will correctly
# figure out that 29 < 3 is FALSE and will correctly tell the user
# "lower, guess again".
#-------------------------------------------------------------------------
guessingGame <- function(low=1, high=100){
if (!is.numeric(low) || length(low) != 1 || trunc(low) != low ||
!is.numeric(high) || length(high) != 1 || trunc(high) != high ) {
stop("min and max must each be single whole numbers")
}
if (low >= high){
stop("low must be less than high")
}
num <- sample(low:high, 1)
numGuesses <- 1
guess <- readline("guess: ")
guess <- as.numeric(guess) # IMPORTANT LINE - see the NOTE in comments above
while(guess != num) {
if (guess < num){
guess <- readline("higher, guess again: ")
} else if (guess > num) {
guess <- readline("lower, guess again: ")
}
guess <- as.numeric(guess) # IMPORTANT LINE - see comment above for more info
numGuesses <- numGuesses + 1
}
return(numGuesses)
}
#guessingGame()
#guessingGame()
#guessingGame()#-------------------------------------
# Another example - Fibonacci sequence
#-------------------------------------
# The numbers 0 1 1 2 3 5 8 13 21 34 55 89 144 ...
# are the first few number in the infinite sequence
# of "Fibonacci numbers". The first two numbers are 0 and 1
# Every other number in the sequence is the sum of the
# two numbers that precede it.
#
# Write a function fib(n) that returns the first
# n numbers from the fibonacci sequence.
#
# n is expected to be a single non-negative whole number.
# The function should stop with an appropriate
# error message if it is not.
#
# EXAMPLE:
# > fib(1)
# 0
#
# > fib(4)
# 0 1 1 2
#
# > fib(8)
# 0 1 1 2 3 5 8 13
fib <- function(n){
if (!is.numeric(n) || length(n) != 1 || n <= 0){
stop("n must be a single whole non-negative number")
}
if (n == 1){
return(0)
} else if (n == 2) {
return(c(0,1))
}
# set up the variables for the condition in the while
# (n already has a value since it is an argument to the function)
answer <- c(0,1)
while(length(answer) < n){ # a condition that will eventually become FALSE
twoPrevious <- answer[length(answer)-1]
onePrevious <- answer[length(answer)]
# change a variable that's in the condition
answer<-c(answer,onePrevious+twoPrevious)
}
return(answer)
}
fib(0)Error in fib(0): n must be a single whole non-negative number
[1] 0
[1] 0 1 1 2
[1] 0 1 1 2 3 5 8 13
[1] 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
[16] 610 987 1597 2584 4181
[[1]]
[1] 0
[[2]]
[1] 0 1
[[3]]
[1] 0 1 1
[[4]]
[1] 0 1 1 2
[[5]]
[1] 0 1 1 2 3
[[6]]
[1] 0 1 1 2 3 5
[[7]]
[1] 0 1 1 2 3 5 8
[[8]]
[1] 0 1 1 2 3 5 8 13
[[9]]
[1] 0 1 1 2 3 5 8 13 21
[[10]]
[1] 0 1 1 2 3 5 8 13 21 34
rm(list=ls()) # start over ...
# The following code was already created above. This is the exact same
# code. It is copied here for reference, since the next function,
# primesUpTo, calls this code.
is.prime <- function( num ) {
if (num < 2){
return(FALSE)
}
divisor <- 2
while ( divisor <= sqrt(num) ) {
if (num %% divisor == 0){
return(FALSE)
}
divisor <- divisor + 1
}
return(TRUE)
}
#-----------------------------------------
# Get all primes up to a certain number
#-----------------------------------------
# Things to think about in the next function.
#
# 1. What would happen if the line: numToCheck = numToCheck + 1
# were placed inside of the block of code for the if?
# All primes up to n
primesUpTo = function( maxNum ){
primes = numeric(0)
# Set up the variables that are used in the condition
# maxNum already has a value since it is an argument to the function
numToCheck = 2
while (numToCheck <= maxNum){ # a condition that will eventually become FALSE
if(is.prime(numToCheck)){
primes = c(primes, numToCheck)
}
# change a variable that is in the condition in a way that will eventually
# make the condition become FALSE
numToCheck = numToCheck + 1
}
primes
}
primesUpTo(10)[1] 2 3 5 7
[1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
#---------------------------------
# Get first n primes
#---------------------------------
# Things to think about in the following function.
#
# 1. What would happen if the line: numberToCheck = numberToCheck + 1
# was not typed at all?
firstNPrimes = function( numPrimes ){
# This is the variable that will be returned.
# It is also a variable that is used in the condition.
# We MUST give it a value for both of these reasons.
primes = numeric(0)
# Setup any other values that the while loop will need.
numberToCheck = 2
while(length(primes)<numPrimes){ # condition that will evenutually become FALSE
if (is.prime(numberToCheck)){
primes = c(primes, numberToCheck)
}
# change a variable that is used the the condition in a way that
# eventually the condition will become FALSE
numberToCheck = numberToCheck + 1
}
primes
}
firstNPrimes(10) [1] 2 3 5 7 11 13 17 19 23 29
[1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71
[1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61
[19] 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151
[37] 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251
[55] 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359
[73] 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463
[91] 467 479 487 491 499 503 509 521 523 541
[1] 3000
[1] 27337 27361 27367 27397 27407 27409 27427 27431 27437 27449
#############################################################.
# The following example is interesting but doesn't add any new
# concepts.
#
# 2022 - We skipped going over this example in class for both
# Wilf and Beren classes but I told students to look at this example
# on their own.
#
# -Prof. Rosenthal
#############################################################.
#---------------------------------------------------------------------
# How many flights are needed before you get back to the city you
# started?
#---------------------------------------------------------------------
# Given the following data:
# The vector cities has names and values that are the same set of cities
# just arranged in a different order. Interpret the names of the vector
# positions as the cities where an airline has flights. The management of
# the company schedules the flights based on the data in the vector.
# (see below for more info)
cities = c("new york", "london", "tokyo", "l.a.", "tel aviv", "brussels", "moscow")
names(cities) = sort(cities)
cities brussels l.a. london moscow new york tel aviv tokyo
"new york" "london" "tokyo" "l.a." "tel aviv" "brussels" "moscow"
# The airline has a complex scheduling system for each of their planes.
# Each plane flies from city to city to city based on the data shown above.
# Eventually a plane will return to its original city.
#
# For example, a plane that
# starts in Brussels will fly to New York
# From New york, that same plane will fly to Tel Aviv.
# From Tel Aviv, that same plane will fly back to Brussels
# for a total of 3 flights before it returns to where it started.
#
# Similarly, a plane that
# starts in L.A. office will fly to London.
# From London, that same plane will fly to Tokyo.
# From Tokyo, that same plane will fly to Moscow.
# From Moscow that same plane will fly back to L.A.
# for a total of 4 flights before it returns to where it started.
#
# Write a function
# flightsBeforeReturning = function(staringCity, schedulingVector)
#
# that figures out how many flights it will take for a plane that
# starts in startingCity will have to fly before it returns to the same
# startingCity based on the data in the schedulingVector.
flightsBeforeReturning = function(startingCity, schedulingVector){
currentCity = startingCity
numberOfFlights = 0
while( schedulingVector[ currentCity ] != startingCity ){
currentCity = schedulingVector[ currentCity ]
numberOfFlights = numberOfFlights + 1
}
if (schedulingVector[startingCity] != startingCity)
numberOfFlights = numberOfFlights + 1
numberOfFlights
}
cities brussels l.a. london moscow new york tel aviv tokyo
"new york" "london" "tokyo" "l.a." "tel aviv" "brussels" "moscow"
[1] 3
[1] 4
[1] 4
[1] 4
[1] 3
[1] 3
[1] 4
# Similar function to above, but this time return the actual sequence of
# cities that are visited
itinerary = function(startingCity, schedulingVector){
currentCity = startingCity
visitedCities = startingCity
while( schedulingVector[ currentCity ] != startingCity ){
visitedCities = c(visitedCities, schedulingVector[currentCity])
currentCity = schedulingVector[ currentCity ]
}
# Add the last leg of the itinerary but only if we flew SOMEWHERE first
if (schedulingVector[startingCity] != startingCity){
visitedCities = c(visitedCities, startingCity)
}
names(visitedCities) = NULL # remove the names
visitedCities
}
cities brussels l.a. london moscow new york tel aviv tokyo
"new york" "london" "tokyo" "l.a." "tel aviv" "brussels" "moscow"
[1] "brussels" "new york" "tel aviv" "brussels"
[1] "l.a." "london" "tokyo" "moscow" "l.a."
[1] "london" "tokyo" "moscow" "l.a." "london"
[1] "moscow" "l.a." "london" "tokyo" "moscow"
[1] "new york" "tel aviv" "brussels" "new york"
[1] "tel aviv" "brussels" "new york" "tel aviv"
[1] "tokyo" "moscow" "l.a." "london" "tokyo"
Suppose we wanted to get multiple factorials and put them in a vector. We could do so by putting the code for a while loop inside the body of another while loop (i.e. this is called a nested loop). We will explore how to do that in the next section. However, nested loops can be confusing, especially for new programmers.
For now, a simpler way to get the factorials of each value in a numeric vector is to use the lapply function. (For a review of lapply see this section.
#----------------------------------------------------------------
# The myFactorial function above only works with a single number.
# You can get the factorials of many numbers by using lapply
# to get a list of the answers for several numbers
#----------------------------------------------------------------
lapply(c(1,3,5,10), myFactorial) # find the myFactorials of 1,2,3 and 4Error: object 'myFactorial' not found
#---------------------------------------------------------------------
# sapply
#---------------------------------------------------------------------
# The sapply function is similar to the lapply function.
#
# The "l" in lapply stands for "list" since the return value of
# lapply is always a list.
#
# The "s" in sapply stands for "simplify". The idea of sapply is that
# sapply can sometimes return the data in a "simpler" structure
# than lapply does. In this regard, vectors and matrices are
# considered "simpler" than lists.
#
# sapply processes the data in a similar way to lapply. Each value
# in the vector is passed to the specified function. The return values
# from the function calls are then gathered into a single object
# (a list, a matrix or a vector) in the following way:
#
# - If every "answer" (i.e. the return value from the function call)
#is a single vector of length 1, then sapply will return
# all of the answers in a single vector instead of returning a list of answers.
#
# - If every "answer" is a vector that has more than one value but all answers
# are the same length (e.g. all answers have 2 values or all answers have 3 values, etc)
# then sapply returns a matrix. Each column in the matrix will contain one of the answers.
#
# - If different "answers" are of different lengths or different classes
# of data, then sapply returns a list of answers. In this case, sapply
# and lapply return the same value.
#
# The "s" in "sapply" stands for "simplify". In other words, sapply
# might return a vector or a matrix, instead of a list. Vectors and matrices
# can be thought of as "simpler" data structures than lists, hence the name "sapply".
#
# The "l" in "lapply" stands for "list". This is because the result
# of calling lapply is ALWAY a list of answers.
#---------------------------------------------------------------------
# compare the following call to sapply with the next call to lapply
# sapply returns a vector in this case since myFactorial always returns a single number
sapply(c(1,3,5,10), myFactorial) Error: object 'myFactorial' not found
# lapply always returns a list of answers (remember "l" stands for "list")
lapply(c(1,3,5,10), myFactorial) Error: object 'myFactorial' not found
As mentioned above, depending on the return values from the function calls, sapply might return a vector, a matrix or a list.
As shown above with the myFactorial function, when all function calls return a single value, sapply returns a vector.
The examples below show that when all function calls return vectors of more than one value, but all of the same length, then sapply returns a matrix.
The examples below also show that when the function calls return vectors of different lengths, or different classes of data (e.g. dataframes, lists, etc), then sapply returns a list.
#..................................................................
# These values and functions will be used below to demonstrate the
# differences between the lapply and sapply functions.
#..................................................................
# the following function always returns a vector of 3 numbers
# regardless of the value of num. See examples below.
getThreeNumbers = function(num){
if(length(num) > 1 || !is.numeric(num)){
stop("num is expected to be a single number")
}
c(num, num+10, num+100)
}
getThreeNumbers(2)[1] 2 12 102
[1] 3 13 103
[1] 5 15 105
# the following function returns different length vectors
# for different values of num. See examples below.
repNumTimes = function(num){
if(length(num) > 1 || !is.numeric(num)){
stop("num is expected to be a single number")
}
rep(num, num)
}
repNumTimes(2)[1] 2 2
[1] 3 3 3
[1] 5 5 5 5 5
# These are some numbers we will use with the following examples
nums = c(1,3,5,10)
# Function f always returns 3 numbers so sapply returns a matrix of 3 rows.
# There will be as many columns in the matrix as there are numbers in nums.
lapply(nums, getThreeNumbers) # a list[[1]]
[1] 1 11 101
[[2]]
[1] 3 13 103
[[3]]
[1] 5 15 105
[[4]]
[1] 10 20 110
[,1] [,2] [,3] [,4]
[1,] 1 3 5 10
[2,] 11 13 15 20
[3,] 101 103 105 110
# Function g returns different length vectors for different numbers
# so sapply returns a list, just as lapply does.
lapply(nums, repNumTimes) # a list[[1]]
[1] 1
[[2]]
[1] 3 3 3
[[3]]
[1] 5 5 5 5 5
[[4]]
[1] 10 10 10 10 10 10 10 10 10 10
[[1]]
[1] 1
[[2]]
[1] 3 3 3
[[3]]
[1] 5 5 5 5 5
[[4]]
[1] 10 10 10 10 10 10 10 10 10 10
The following examples show how to use lapply and sapply with the is.prime function.
The following gets the first 20 numbers and checks if they are prime. It’s relatively easy to see which number is prime and which is not as the results are in order - 1st answer is TRUE or FALSE for 1, 2nd answer is TRUE or FALSE for 2, etc.
[[1]]
[1] FALSE
[[2]]
[1] TRUE
[[3]]
[1] TRUE
[[4]]
[1] FALSE
[[5]]
[1] TRUE
[[6]]
[1] FALSE
[[7]]
[1] TRUE
[[8]]
[1] FALSE
[[9]]
[1] FALSE
[[10]]
[1] FALSE
[[11]]
[1] TRUE
[[12]]
[1] FALSE
[[13]]
[1] TRUE
[[14]]
[1] FALSE
[[15]]
[1] FALSE
[[16]]
[1] FALSE
[[17]]
[1] TRUE
[[18]]
[1] FALSE
[[19]]
[1] TRUE
[[20]]
[1] FALSE
The following gets the prime status of the first few odd numbers. It’s harder to see which number is prime and which is not as the first TRUE/FALSE is for 1, the second TRUE/FALSE is for 3, the third TRUE/FALSE is for 5, etc.
[[1]]
[1] FALSE
[[2]]
[1] TRUE
[[3]]
[1] TRUE
[[4]]
[1] TRUE
[[5]]
[1] FALSE
[[6]]
[1] TRUE
[[7]]
[1] TRUE
[[8]]
[1] FALSE
[[9]]
[1] TRUE
[[10]]
[1] TRUE
We can make it easier to see which number is prime and which is not by creating a named vector where the name of each element in the vector is the number that was checked.
# add names
x = lapply(seq(1,19,by=2), is.prime)
names(x) = seq(1,19,by=2)
x # names must start with a letter. If they don't you can include the name in `backticks`$`1`
[1] FALSE
$`3`
[1] TRUE
$`5`
[1] TRUE
$`7`
[1] TRUE
$`9`
[1] FALSE
$`11`
[1] TRUE
$`13`
[1] TRUE
$`15`
[1] FALSE
$`17`
[1] TRUE
$`19`
[1] TRUE
[1] FALSE
[1] TRUE
#------------------------------------------.
# Use sapply to get multiple results
#------------------------------------------.
sapply(1:20, is.prime) # result is below [1] FALSE TRUE TRUE FALSE TRUE FALSE TRUE FALSE FALSE FALSE TRUE FALSE
[13] TRUE FALSE FALSE FALSE TRUE FALSE TRUE FALSE
# FALSE TRUE TRUE FALSE TRUE FALSE TRUE FALSE FALSE FALSE TRUE FALSE TRUE FALSE FALSE FALSE TRUE FALSE TRUE FALSE
sapply(seq(1,19,by=2), is.prime) # # check just the odd numbers [1] FALSE TRUE TRUE TRUE FALSE TRUE TRUE FALSE TRUE TRUE
# [1] FALSE TRUE TRUE TRUE FALSE TRUE TRUE FALSE TRUE TRUE
# add names
x = sapply(seq(1,19,by=2), is.prime)
names(x) = seq(1,19,by=2)
x # result is below 1 3 5 7 9 11 13 15 17 19
FALSE TRUE TRUE TRUE FALSE TRUE TRUE FALSE TRUE TRUE
# 1 3 5 7 9 11 13 15 17 19
# FALSE TRUE TRUE TRUE FALSE TRUE TRUE FALSE TRUE TRUE
x[9] # result is below - we only checked odd numbers so the 9th number checked is 17 17
TRUE
9
FALSE
#####################################################################
#####################################################################
##
## NESTED LOOPS
##
## A "nested loop" is a loop inside of another loop (similar
## to "nested ifs")
##
## We often refer to the "outer loop" and the "inner loop".
##
#####################################################################
#####################################################################
rm(list=ls()) # start over
example = function( maxOuter, maxInner){
outer = 1
while(outer <= maxOuter){
cat("OUTER LOOP (before inner loop): outer=", outer, "\n\n")
inner = 1
while(inner <= maxInner){
cat(" INNER LOOP: outer=",outer,"inner=",inner,"\n")
inner = inner + 1
}
outer = outer + 1
}
}
example(maxOuter = 2, maxInner = 3)OUTER LOOP (before inner loop): outer= 1
INNER LOOP: outer= 1 inner= 1
INNER LOOP: outer= 1 inner= 2
INNER LOOP: outer= 1 inner= 3
OUTER LOOP (before inner loop): outer= 2
INNER LOOP: outer= 2 inner= 1
INNER LOOP: outer= 2 inner= 2
INNER LOOP: outer= 2 inner= 3
OUTER LOOP (before inner loop): outer= 1
INNER LOOP: outer= 1 inner= 1
INNER LOOP: outer= 1 inner= 2
INNER LOOP: outer= 1 inner= 3
INNER LOOP: outer= 1 inner= 4
INNER LOOP: outer= 1 inner= 5
OUTER LOOP (before inner loop): outer= 2
INNER LOOP: outer= 2 inner= 1
INNER LOOP: outer= 2 inner= 2
INNER LOOP: outer= 2 inner= 3
INNER LOOP: outer= 2 inner= 4
INNER LOOP: outer= 2 inner= 5
OUTER LOOP (before inner loop): outer= 3
INNER LOOP: outer= 3 inner= 1
INNER LOOP: outer= 3 inner= 2
INNER LOOP: outer= 3 inner= 3
INNER LOOP: outer= 3 inner= 4
INNER LOOP: outer= 3 inner= 5
###############################################################################
#
# Examples of nested loops
#
###############################################################################
rm(list=ls()) # start over ...
#-----------------------------------------------------------------------------
# Above we defined a
#
# primesUpTo = function( n )
#
# That returns a vector of all the primes up to n. For example:
#
# > primesUpTo(15)
# [1] 2 3 5 7 11 13
#
# In that version of the function, we used a single loop. We also
# called the function is.prime that we had defined earlier inside of the loop.
# Both the funciton is.prime and the function primesUpTo, used a single
# loop for each function.
#
#
# ***********************************************************************
# *** THE FOLLOWING IS ANOTHER WAY OF WRITING THE SAME FUNCTION. ***
# *** THIS VERSION DOES NOT CALL is.prime AT ALL. RATHER THIS SINGLE ***
# *** FUNCTION DOES ALL OF THE WORK USING TWO DIFFERENT LOOPS - ***
# *** ONE INSIDE THE OTHER (i.e. a "nested loop") ***
# ***********************************************************************
#
# The following function, primesUpTo_nestedLoops, returns the exact same values
# as the primesUpTo function above. However, this version of the function
# does NOT call is.prime. Rather, this version calculates whether a number
# is prime directly in the same function by using a nested loop (i.e.
# one loop inside of another loop)
#-----------------------------------------------------------------------------
rm(list=ls())
# VERSION WITH "nested loops"
primesUpTo_nestedLoops = function( maxNum ){
primes = numeric(0)
numToCheck = 2
while (numToCheck <= maxNum){
# Use an inner loop to figure out if numToCheck is in fact prime
# and if it is add numToCheck to the vector primes
isPrime = TRUE
divisor = 2
while(divisor < numToCheck){
if( numToCheck %% divisor == 0){
isPrime = FALSE
}
divisor = divisor+1
}
# Add the numToCheck to the primes if it in fact is prime
if(isPrime) {
primes = c(primes, numToCheck)
}
numToCheck = numToCheck + 1
}
primes
}
primesUpTo_nestedLoops(10)[1] 2 3 5 7
[1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
We wrote the firstNPrimes function above by using the function is.prime that we had created earlier. Each function, is.prime and firstNPrimes used a single loop.
Rewrite the function firstNPrimes. However, this time the firstNPrimes should not call the is.prime function. Rather write the firstNPrimes function to use a nested loop.
The outside loop should keep looping until N primes have been found. Each time through the outer loop another number should be checked to see if it is prime.
Use an “inner” loop to deterimine if the number that is currently being evaluated is prime. This inner loop should run to completion each time through the outer loop. This inner loop essentially does the job of the is.prime function.
NOTE: It can be argued that the “original” version of the code is better than coding a single function with a nested loop. Having two simple functions can be easier to understand than coding a single more complicated function with a nested loop.
Nevertheless, others might argue that having a single function where everything is in one place is better.
Bottom line - they are both valid approaches to this problem and you need to understand both.
#~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
# For your reference, below are the original versions of the is.prime function
# and the firstNPimes function that calls the is.prime function and does NOT
# use nested loops.
#~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
rm(list=ls()) # start over
is.prime <- function( num ) {
if (num < 2){
return(FALSE)
}
divisor <- 2
while ( divisor <= sqrt(num) ) {
if (num %% divisor == 0){
return(FALSE)
}
divisor <- divisor + 1
}
return(TRUE)
}
firstNPrimes = function( numPrimes ){
primes = numeric(0)
numberToCheck = 2
while ( length(primes) < numPrimes ){
if (is.prime(numberToCheck)){
primes = c(primes, numberToCheck)
}
numberToCheck = numberToCheck + 1
}
primes
}
firstNPrimes(10) [1] 2 3 5 7 11 13 17 19 23 29
[1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61
[19] 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151
[37] 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251
[55] 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359
[73] 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463
[91] 467 479 487 491 499 503 509 521 523 541
rm(list=ls())
firstNPrimes = function( numPrimes ){
primes = numeric(0)
numberToCheck = 2
while ( length(primes) < numPrimes ){
isPrime = TRUE
# Check if numberToCheck is prime. If it isn't prime set the variable
# isPrime to FALSE.
divisor = 2
while(divisor < numberToCheck){
if ( numberToCheck %% divisor == 0){
isPrime = FALSE
}
divisor = divisor + 1
}
if (isPrime == TRUE){
primes = c(primes, numberToCheck)
}
numberToCheck = numberToCheck + 1
}
primes
}
firstNPrimes(10) [1] 2 3 5 7 11 13 17 19 23 29
[1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61
[19] 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151
[37] 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251
[55] 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359
[73] 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463
[91] 467 479 487 491 499 503 509 521 523 541
The t function takes a matrix and returns a copy of the matrix with the rows and columns swapped, i.e. mat[i,j] becomes returnValue[j,i]. See the example below.
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 10 40 70 100 130 160
[2,] 20 50 80 110 140 170
[3,] 30 60 90 120 150 180
[,1] [,2] [,3]
[1,] 10 20 30
[2,] 40 50 60
[3,] 70 80 90
[4,] 100 110 120
[5,] 130 140 150
[6,] 160 170 180
Write the function myt that does the same thing as the t function. Do NOT call the t function in your code.
Suggestions:
#############.
# ANSWER
#############.
myt = function( m ){
# make the answer the right number of rows and columns
answer = matrix( 0 , nrow=ncol(m) , ncol=nrow(m) )
row = 1
while(row <= nrow(m)) {
col = 1
while(col <= ncol(m)) {
# assign the value at row,col in m to the correct place in the answer
answer[col,row] = m[row,col]
col = col + 1
}
row = row + 1
}
answer
}
#---------------------------
# Test our function
#---------------------------
# Create a sample matrix
mat = matrix(seq(10,120,10), nrow=3, ncol=4)
mat [,1] [,2] [,3] [,4]
[1,] 10 40 70 100
[2,] 20 50 80 110
[3,] 30 60 90 120
[,1] [,2] [,3]
[1,] 10 20 30
[2,] 40 50 60
[3,] 70 80 90
[4,] 100 110 120
See the previous question.
This time, see if you can rewrite the function myt to use a single loop (not a nested loop).
Suggestions:
The single loop should keep updating a variable, eg. inRow, that contains the number of a row from the input matrix. For example
Inside the loop use matrix notation to assign the values from a row in the input matrix to the corresponding column in the output matrix
myt=function( m ){
returnValue = matrix(1 ,nrow=ncol(m) ,ncol=nrow(m))
inRow = 1
while(inRow <= nrow(m)) {
returnValue[ , inRow] = m[inRow , ]
inRow = inRow + 1
}
returnValue
}
mat = matrix(seq(10,180,10), nrow=3, ncol=6)
mat [,1] [,2] [,3] [,4] [,5] [,6]
[1,] 10 40 70 100 130 160
[2,] 20 50 80 110 140 170
[3,] 30 60 90 120 150 180
[,1] [,2] [,3]
[1,] 10 20 30
[2,] 40 50 60
[3,] 70 80 90
[4,] 100 110 120
[5,] 130 140 150
[6,] 160 170 180
Below are a few “toy” examples of using nested loops. Exercises like these help you to become more familiar with the concepts of nested loops. This is similar to playing “scales” when learning to play the piano - no one will play scales in real life - but it is important to get the hang of things when you’re first starting out.
The following functions each use a single loop (i.e. NOT nested loops). These are provided for comparison with the equivalent versions that use nested loops which we will show you below.
rm(list=ls()) # start over
# draw a line of x's of the specified width
# Do not use the rep function. Use the cat function and a loop.
drawLine_WithXs = function( width ) {
while(width > 0){
cat("x")
width = width - 1
}
}
drawLine_WithXs(4) # xxxxxxxx
xxxxx
# draw a box of x's
drawBox_WithXs = function(h, w){
while(h > 0){
drawLine_WithXs(w)
cat("\n")
h = h - 1
}
}
drawBox_WithXs(3,4) # box of 3 rows and 4 columns of x'sxxxx
xxxx
xxxx
# Draw a horizontal line with calls to cat that displays the numbers as shown below
drawLine_WithNums = function(width){
num1 = 1
while(num1 <= width){
cat(num1)
num1 = num1 + 1
}
}
drawLine_WithNums(4) # 12341234
12345
# Write a function
# drawBox1 = function(height, width)
#
# height and width are expected to be whole numbers between 1 and 9.
# The function should draw a box that has dimensions height rows and width columns.
# The box should be drawn with numbers such that each number represent the
# number of the column it is in. For example:
#
# > drawBox1(3, 5)
# 12345
# 12345
# 12345#-----------------------------------------------------------------------------
# QUESTION:
# Write a function
# drawBox2 = function(height, width)
#
# height and width are expected to be whole numbers between 1 and 9.
# The function should draw a box that has dimensions height rows and width columns.
# The box should be drawn with numbers such that each number represent the
# number of the row it is in. For example:
#
# > drawBox1(3, 5)
# 11111
# 22222
# 33333
#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------
# Write the function drawBox3 to produce the results according to the pattern
# demonstrated in the following example:
#
# EXAMPLE:
# > drawBox3(3,4)
# 3333
# 2222
# 1111
#-----------------------------------------------------------------------------Write the function drawBox4 to produce the results according to the pattern demonstrated in the following example:
> drawBox4(3,4)
4321
4321
4321
# Write the function drawTriangle1 to produce the results according to the pattern
# demonstrated in the following example:
#
# EXAMPLE:
# > drawTriangle1(3)
# 1
# 12
# 123
#
# > drawTriangle(5)
# 1
# 12
# 123
# 1234
# 12345
#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------
# Write the function drawTriangle1 to produce the results according to the pattern
# demonstrated in the following example:
#
# EXAMPLE:
# > drawTriangle2(3)
# 111
# 22
# 3
#-----------------------------------------------------------------------------In R there are a few ways to do something over and over again … Often it is possible to recreate the same effect as a while loop by using one of these other approaches.
The apply family of R functions, e.g. lapply and sapply can very often be used instead of a loop. These functions perform a function for each value in a vector (or a list).
R has other types of loops - e.g. for loop (covered in another chapter) and repeat loop (not covered in this book)
© 2025 Y. Rosenthal. All rights reserved.