<- initial
iterator
while (condition) {
do_something<- iterator + 1
iterator }
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:
<- 1
i while (i <= 3) {
cat(sprintf("%d, ", i))
<- i + 1
i
}#> 1, 2, 3,
# For comparison
<- 1
i while (i <= 3) {
print(i)
<- i + 1
i
}#> [1] 1
#> [1] 2
#> [1] 3
cat
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
= letters[3:1]
x
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
= sample.int(n = 100, size = 10, replace = FALSE)
random_numbers = sort(random_numbers)
random_numbers
random_numbers#> [1] 1 18 31 34 46 56 68 92 97 98
# initialize object of cumulative sum
= 0
total_sum
# declare iterator
= 0
pos
# repetitive steps
while (total_sum <= 100) {
= pos + 1
pos = total_sum + random_numbers[pos]
total_sum
}
# what is the value of the cumulative sum?
total_sum#> [1] 130
# how many iterations were necessary?
pos#> [1] 5
The next and break statements
next
– skipping certain iterationsbreak
– stop a loop from iterating
Loop using repeat
Example of using repeat
and break
:
= 42
x
repeat {
print(x)
if (x < 1) {
break
}= sqrt(x) - 1
x
}#> [1] 42
#> [1] 5.480741
#> [1] 1.341098
#> [1] 0.1580579
# Equivalently,
= 42
x
while (x >=1){
if(x==42) print(x)
= sqrt(x) - 1
x print(x)
}#> [1] 42
#> [1] 5.480741
#> [1] 1.341098
#> [1] 0.1580579
More About Functions: Nested Functions
Example 15.1.2
= function(a) {
getmax # nested function
<- function(u){
maxpos which.max(u)
}
# output
list(position = maxpos(a),
value = max(a))
}getmax(c(2, -4, 6, 10, pi))
#> $position
#> [1] 4
#>
#> $value
#> [1] 10
More 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.
<- 10
x <- 20
y
<- function() {
g02 <- 1
x <- 2
y c(x, y)
}
g02()
#> [1] 1 2
<- 2
x <- function() {
g03 <- 1
y c(x, y)
}g03()
#> [1] 2 1
x#> [1] 2
y#> [1] 20
Functions versus variables
- Function_names defined inside a function mask function_names defined outside a function.
<- function(x) x + 1
g07 <- function() {
g08 <- function(x) x + 100
g07 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.
<- function(x) x + 100
g09 <- function() {
g10 <- 10
g09 g09(g09)
}g10()
#> [1] 110
A fresh start
<- function() {
g11 if (!exists("a")) {
<- 1
a else {
} <- a + 1
a
}
a
}
g11()
#> [1] 1
g11()
#> [1] 1
Dynamic lookup
<- function() x + 1
g12 <- 15
x g12()
#> [1] 16
#> [1] 16
<- 20
x g12()
#> [1] 21
#> [1] 21
More About Functions: Signalling Conditions
<- function(x){
is_even_or_odd 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 number
stop()
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] 4
More 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] 10
More 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[!is.na(x)]
x
}<- length(x)
total <- 0
x_sum for (i in seq_along(x)) {
<- x_sum + x[i]
x_sum
}/ total
x_sum }
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[!is.na(x)]
x
}<- length(x)
total <- 0
x_sum for (i in seq_along(x)) {
<- x_sum + x[i]
x_sum
}/ total
x_sum }
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