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 orxor
logical 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.
error
warning
message
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"