3. Control Structures
Control structures are used to control the flow of your code or program. Basically, control structures are used to decide what to do; as basic as they are, control structures are the basis of intelligence in computer programs.
3.1. if-else
In the if-else statement, you must evaluate an expression that will return a boolean (TRUE or FALSE) value. Typically, expressions are evaluated using mathematical comparison operators.
<less than>greater than<=less than or equal>=greater than or equal==equals to!=not equals
Here are some example using simple if-else statements to evaluate a variable x satisfies some comparisons.
[1]:
x <- 10
if (x < 10) {
print('x < 10')
} else {
print('x >= 10')
}
[1] "x >= 10"
[2]:
x <- 10
if (x <= 10) {
print('x <= 10')
} else {
print('x > 10')
}
[1] "x <= 10"
[3]:
x <- 10
if (x != 10) {
print('x != 10')
} else {
print('x == 10')
}
[1] "x == 10"
3.1.1. if-elseif-else
The complete if-else statement can have multiple else if blocks.
[4]:
x <- 10
if (x < 10) {
print('x < 10')
} else if (x == 10) {
print('x == 10')
} else {
print('x > 10')
}
[1] "x == 10"
[5]:
x <- 12
if (x < 10) {
print('x < 10')
} else if (x == 10) {
print('x == 10')
} else if (x < 12) {
print('x < 12')
} else {
print('x >= 12')
}
[1] "x >= 12"
3.1.2. ifelse
The ifelse function provides (what is known in other programming languages as) a ternary operator.
[6]:
x <- 2
y <- ifelse(x %% 2 == 0, 'even', 'odd')
y
3.1.3. Logical operators
You may chain comparison operators through logical operators.
!logical not&&logical and||logical orxorlogical xor
Below are some examples using these logical operators.
[7]:
isMale = FALSE
if (!isMale) {
print('not male')
} else {
print('male')
}
[1] "not male"
[8]:
isMale = FALSE
isAdult = TRUE
if (isMale && isAdult) {
print('male adult')
} else if (isMale && !isAdult) {
print('male minor')
} else if (!isMale && isAdult) {
print('female adult')
} else {
print('female minor')
}
[1] "female adult"
[9]:
isHungry = TRUE
isThirsty = FALSE
if (isHungry || isThirsty) {
print('time for some grub')
} else {
print('keep working')
}
[1] "time for some grub"
[10]:
isHungry = TRUE
isThirsty = TRUE
if (xor(isHungry, isThirsty)) {
print('time for some grub')
} else {
print('you waited too long to eat')
}
[1] "you waited too long to eat"
3.2. For loops
3.2.1. Looping over a vector
[11]:
names <- c('John', 'Jane', 'Jack', 'Joyce')
for (name in names) {
print(name)
}
[1] "John"
[1] "Jane"
[1] "Jack"
[1] "Joyce"
3.2.2. Looping over a range of numbers
[12]:
for (i in 1:10) {
print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
3.2.3. sapply and lapply
You may also loop over elements in a vector using the sapply and lapply functions. The sapply function returns a vector and the lapply returns a list.
Typically, you should favor sapply and lapply over a for loop as these functions are vectorized. When you perform vectorized operations, they are faster since they are done in parallel, but the context will determine which is appropriate for your use.
[13]:
names <- c('John', 'Jane', 'Jack', 'Joyce')
a <- lapply(names, print)
print(a)
[1] "John"
[1] "Jane"
[1] "Jack"
[1] "Joyce"
[[1]]
[1] "John"
[[2]]
[1] "Jane"
[[3]]
[1] "Jack"
[[4]]
[1] "Joyce"
[14]:
names <- c('John', 'Jane', 'Jack', 'Joyce')
a <- sapply(names, print)
print(a)
[1] "John"
[1] "Jane"
[1] "Jack"
[1] "Joyce"
John Jane Jack Joyce
"John" "Jane" "Jack" "Joyce"
Here, we use lapply to apply the sin function to a list of numbers [1:5].
[15]:
a <- lapply(1:5, sin)
print(a)
[[1]]
[1] 0.841471
[[2]]
[1] 0.9092974
[[3]]
[1] 0.14112
[[4]]
[1] -0.7568025
[[5]]
[1] -0.9589243
3.3. While loops
The while loop is another loop structure like the for loop, however, it has a termination condition that must be satisified for the loop to end. In the example below, we loop until x is greater than 5. Note that we increment x at the end of every iteration x <- x + 1.
[16]:
x <- 1
while (x <= 5) {
print(x)
x <- x + 1
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
3.4. Conditions
Conditions may also interrupt the flow of your program. There are three types of conditions.
errorwarningmessage
3.4.1. Error
Use stop to signal an error.
[17]:
a <- 10
b <- 0
if (b == 0) {
stop('divide by zero detected')
} else {
print(a / b)
}
Error in eval(expr, envir, enclos): divide by zero detected
Traceback:
1. stop("divide by zero detected") # at line 5 of file <text>
3.4.2. Warning
[18]:
a <- 10
b <- 0
if (b == 0) {
warning('divide by zero detected, will divide by a small number instead')
print(a / 0.00001)
} else {
print(a / b)
}
Warning message in eval(expr, envir, enclos):
“divide by zero detected, will divide by a small number instead”
[1] 1e+06
You may suppress warnings with supressWarnings.
[19]:
a <- 10
b <- 0
suppressWarnings({
if (b == 0) {
warning('divide by zero detected, will divide by a small number instead')
print(a / 0.00001)
} else {
print(a / b)
}
})
[1] 1e+06
3.4.3. Message
[20]:
a <- 10
b <- 3
if (b == 0) {
stop('divide by zero detected')
} else {
message('parameters are good')
print(paste('final results:', a / b))
}
parameters are good
[1] "final results: 3.33333333333333"
[21]:
a <- 10
b <- 3
suppressMessages({
if (b == 0) {
stop('divide by zero detected')
} else {
message('parameters are good')
print(paste('final results:', a / b))
}
})
[1] "final results: 3.33333333333333"
3.4.4. Handling condtions with try-catch
[22]:
a <- 10
b <- 0
result <- tryCatch({
if (b == 0) {
stop('divide by zero')
} else {
print(a / b)
}
}, warning = function(w) {
print(a / 0.00001)
}, error = function(e) {
print(a / 0.00001)
}, finally = {
print('i am done computing a / b')
})
[1] 1e+06
[1] "i am done computing a / b"