iterator <- initial
while (condition) {
do_something
iterator <- iterator + 1
}While Loop and More about Functions
Reading Assignments
Make sure to read the following sections in the textbook: R Coding Basics, https://www.gastonsanchez.com/R-coding-basics/
While Loop
- Use for loop – when you know how many times a given computation needs to be repeated.
- Use while loop – repeat a process without necessarily knowing how many times this repetition will take place
The generic syntax of while loop is:
Example:
i <- 1
while (i <= 3) {
cat(sprintf("%d, ", i))
i <- i + 1
}
#> 1, 2, 3,
# For comparison
i <- 1
while (i <= 3) {
print(i)
i <- i + 1
}
#> [1] 1
#> [1] 2
#> [1] 3cat vs print
print("I will not buy this record.\n It is scratched.\n")
#> [1] "I will not buy this record.\n It is scratched.\n"
cat("I will not buy this record.\n It is scratched.\n")
#> I will not buy this record.
#> It is scratched.sprintf
x = letters[3:1]
for(i in 1:3){
cat(sprintf("Obs %d is %s.", i, x[i]), "\n")
}
#> Obs 1 is c.
#> Obs 2 is b.
#> Obs 3 is a.Please find more information about sprintf in Section 6.1.4 at https://deepr.gagolewski.com/chapter/160-character.html or help(sprintf)
Example 14.3
set.seed(234) # for replication purposes
# vector of 10 random integers between 1 and 100
random_numbers = sample.int(n = 100, size = 10, replace = FALSE)
random_numbers = sort(random_numbers)
random_numbers
#> [1] 1 18 31 34 46 56 68 92 97 98# initialize object of cumulative sum
total_sum = 0
# declare iterator
pos = 0
# repetitive steps
while (total_sum <= 100) {
pos = pos + 1
total_sum = total_sum + random_numbers[pos]
}
# what is the value of the cumulative sum?
total_sum
#> [1] 130
# how many iterations were necessary?
pos
#> [1] 5The next and break statements
next– skipping certain iterationsbreak– stop a loop from iterating
Loop using repeat
Example of using repeat and break:
x = 42
repeat {
print(x)
if (x < 1) {
break
}
x = sqrt(x) - 1
}
#> [1] 42
#> [1] 5.480741
#> [1] 1.341098
#> [1] 0.1580579
# Equivalently,
x = 42
while (x >=1){
if(x==42) print(x)
x = sqrt(x) - 1
print(x)
}
#> [1] 42
#> [1] 5.480741
#> [1] 1.341098
#> [1] 0.1580579More About Functions: Nested Functions
Example 15.1.2
getmax = function(a) {
# nested function
maxpos <- function(u){
which.max(u)
}
# output
list(position = maxpos(a),
value = max(a))
}
getmax(c(2, -4, 6, 10, pi))
#> $position
#> [1] 4
#>
#> $value
#> [1] 10More About Functions: Lexical Scoping
R’s lexical scoping follows four primary rules:
- Name masking
- Functions versus variables
- A fresh start
- Dynamic lookup
Name Masking
- Name defined inside a function mask names defined outside a function.
x <- 10
y <- 20
g02 <- function() {
x <- 1
y <- 2
c(x, y)
}
g02()
#> [1] 1 2x <- 2
g03 <- function() {
y <- 1
c(x, y)
}
g03()
#> [1] 2 1
x
#> [1] 2
y
#> [1] 20Functions versus variables
- Function_names defined inside a function mask function_names defined outside a function.
g07 <- function(x) x + 1
g08 <- function() {
g07 <- function(x) x + 100
g07(10)
}
g08()
#> [1] 110- When a function and a non-function share the same name, R ignores non-function objects when looking for that value.
g09 <- function(x) x + 100
g10 <- function() {
g09 <- 10
g09(g09)
}
g10()
#> [1] 110A fresh start
g11 <- function() {
if (!exists("a")) {
a <- 1
} else {
a <- a + 1
}
a
}
g11()
#> [1] 1
g11()
#> [1] 1Dynamic lookup
g12 <- function() x + 1
x <- 15
g12()
#> [1] 16
#> [1] 16
x <- 20
g12()
#> [1] 21
#> [1] 21More About Functions: Signalling Conditions
is_even_or_odd <- function(x){
if (!is.integer(x)){
stop("Not a whole number")
} else if (x %% 2 == 0){
return("even")
} else {
return("odd")
}
}
is_even_or_odd(5.5)
#> Error in is_even_or_odd(5.5): Not a whole numberstop()can also be used in for loops.
Example:
for(i in 1:10){
if(i %% 5 == 0){
stop("A multiple of 5") # break & error message
}
print(i)
}
#> [1] 1
#> [1] 2
#> [1] 3
#> [1] 4
#> Error in eval(expr, envir, enclos): A multiple of 5# For comparison purpose
for(i in 1:10){
if(i %% 5 == 0){
break
}
print(i)
}
#> [1] 1
#> [1] 2
#> [1] 3
#> [1] 4More on signalling conditions
There are three conditions that you can signal in code: errors, warnings, and messages.
- Errors are the most severe. Severe issue \(\Rightarrow\) the execution of the program should stop immediately.
- Warnings fall somewhat in between errors and message. Condition is not critical, the program will continue to run, but the user should be alerted about potential issues.
- Messages are the mildest. For informational purposes, the flow of the program is not interfered.
Please find more information at https://adv-r.hadley.nz/conditions.html?q=while#signalling-conditions
Warning
for(i in 1:10){
if(i %% 5 == 0){
warning("A multiple of 5")
}
print(i)
}
#> [1] 1
#> [1] 2
#> [1] 3
#> [1] 4
#> Warning: A multiple of 5
#> [1] 5
#> [1] 6
#> [1] 7
#> [1] 8
#> [1] 9
#> Warning: A multiple of 5
#> [1] 10- Message
for(i in 1:10){
if(i %% 5 == 0){
message("A multiple of 5")
}
print(i)
}
#> [1] 1
#> [1] 2
#> [1] 3
#> [1] 4
#> A multiple of 5
#> [1] 5
#> [1] 6
#> [1] 7
#> [1] 8
#> [1] 9
#> A multiple of 5
#> [1] 10More About Functions: For Readable Codes
White Spaces
Example:
if (!is.vector(x)) {
stop('x must be a vector')
} else {
if (any(is.na(x))) {
x <- x[!is.na(x)]
}
total <- length(x)
x_sum <- 0
for (i in seq_along(x)) {
x_sum <- x_sum + x[i]
}
x_sum / total
}Left parenthesis should start immediately after a function name
# NOT Recommended
read.table ('data.csv', header = TRUE, row.names = 1)
# Recommended
read.table('data.csv', header = TRUE, row.names = 1)Indention
Example:
# better with indentation
if (!is.vector(x)) {
stop('x must be a vector')
} else {
if (any(is.na(x))) {
x <- x[!is.na(x)]
}
total <- length(x)
x_sum <- 0
for (i in seq_along(x)) {
x_sum <- x_sum + x[i]
}
x_sum / total
}Section 15.3.1 Meaningful Names https://www.gastonsanchez.com/R-coding-basics/5-06-more-functions.html#meaningful-names
Section 15.3.2 Syntax: Parentheses https://www.gastonsanchez.com/R-coding-basics/5-06-more-functions.html#syntax-parentheses