4  Estructuras de control

Como en otros lenguajes de programación, en R existen instrucciones para controlar el flujo de ejecución de un programa. Básicamente existen dos tipos:

4.1 Estructuras condicionales

Las estructuras condicionales permiten evaluar el estado del programa y tomar decisiones sobre qué código ejecutar en función del mismo.

4.1.1 Condicionales (if)

La principal estructura condicional comienza con la palabra reservada if, lleva asociada expresión de tipo lógico o booleano y permite ejecutar un bloque de código dependiendo de si la evaluación de esa expresión es TRUE o FALSE.

if (<exp>) {
  <código>
}

Si el resultado de evaluar la expresión <exp> es TRUE entonces se ejecuta el código <código>, mientras que si es FALSE no.

Diagrama de flujo de la estructura condicional simple

Diagrama de flujo de la estructura condicional simple

Ejemplo 4.1  

x <- 1
y <- 0
if (y != 0){
  print(x / y)
}

Si se desea ejecutar un bloque de código alternativo cuando no se cumpla la condición se puede añadir a continuación con la palabra reservada else.

if (<exp>) {
  <código 1>
} else {
  <código 2>
}

En este caso, si la evaluación de la condición es TRUE se ejecuta el código <código 1> y si es FALSE se ejecuta el código <código 2>.

Diagrama de flujo de la estructura condicional doble

Ejemplo 4.2  

nota <- 8.5
if (nota < 5){
  print("Suspenso")
} else {
  print("Aprobado")
}
[1] "Aprobado"

Se puede comprobar más de una condición encadenando otra instrucción if tras las instrucción else.

if (<exp 1>) {
  <código 1>
} else if (<exp 2>) {
  <código 2>) {

} else {
  <código n>
}

Cuando se encadenan múltiples condiciones de esta forma, solamente se ejecuta el bloque de código asociado a la primera condición cuya evaluación sea TRUE. El último bloque de código solamente se ejecuta si todas las condiciones son falsas.

Diagrama de flujo de la estructura condicional múltiple

Ejemplo 4.3  

nota <- 8.5
if (nota < 5){
  print("Suspenso")
} else if (nota < 7) {
  print("Aprobado")
} else if (nota < 9) {
  print("Notable")
} else {
  print("Sobresaliente")
}
[1] "Notable"

4.1.2 La función switch()

Otra forma de tomar decisiones sobre el código a ejecutar es la función switch.

  • switch(x, l): Ejecuta el código del valor de la lista l cuyo nombre asociado coincide con el resultado de evaluar la expresión x. Si el resultado de evaluar x no es ningún nombre de los elementos de la lista devuelve NULL.

Ejemplo 4.4  

tipo.iva <- "reducido"
precio <- 1000
iva <- precio * switch(tipo.iva, "superreducido" = 4, "reducido" = 10, "normal" = 21) / 100
iva
[1] 100

4.2 Bucles

Un bucle es una estructura que permite la repetición de un bloque de código. En R existen dos tipos de bucles, los bucles iterativos y los bucles condicionales.

4.2.1 Bucles iterativos (for)

Lo bucles iterativos repiten un bloque de código un número determinado de veces. Comienzan por la palabra reservada for y llevan asociado un iterador, que es una variable que recorre una secuencia de un tipo de datos compuesto, normalmente un vector o una lista. El bloque de código se ejecuta tantas veces como elementos tenga la secuencia, y en cada repetición el iterador toma como valor un elemento distinto de la secuencia.

for (i in <secuencia>) {
  <código>
}

Diagrama de flujo de un bucle iterativo

Ejemplo 4.5 A continuación se muestra varios ejemplos de uso del bucle for.

asignaturas <- c("Matemáticas", "Física", "Programación")
for (i in asignaturas) {
  print(i)
}
[1] "Matemáticas"
[1] "Física"
[1] "Programación"
for (i in 1:5) {
  print(paste("El cuadrado de ", i, " es ", i^2))
}
[1] "El cuadrado de  1  es  1"
[1] "El cuadrado de  2  es  4"
[1] "El cuadrado de  3  es  9"
[1] "El cuadrado de  4  es  16"
[1] "El cuadrado de  5  es  25"

También es posible recorrer los elementos de la secuencia por posición ayudándonos de la siguiente función:

  • seq_along(x): que devuelve un vector con los enteros desde 1 hasta el número de elementos de la secuencia x.

Ejemplo 4.6  

asignaturas <- c("Matemáticas", "Física", "Programación")
for (i in seq_along(asignaturas)){
  print(paste("Asignatura ", i, ":", asignaturas[i]))
}
[1] "Asignatura  1 : Matemáticas"
[1] "Asignatura  2 : Física"
[1] "Asignatura  3 : Programación"

Los bucles iterativos se utilizan habitualmente para recorrer estructuras de una dimensión como los vectores y las listas, donde se sabe de antemano el número de elementos que contiene y, por tanto, el número de iteraciones del bucle. No obstante, también se pueden recorrer estructuras de más de una dimensión, como por ejemplo matrices, utilizando varios bucles for anidados.

Ejemplo 4.7 A continuación se muestra varios ejemplos de dos bucles for anidados para recorrer los elementos de una matriz.

x <- matrix(1:6, 2, 3)
for (i in 1:nrow(x)) {
  for (j in 1:ncol(x)){
    print(x[i,j])
  }
}
[1] 1
[1] 3
[1] 5
[1] 2
[1] 4
[1] 6

4.2.2 Bucles condicionales (while)

Los bucles condicionales repiten un bloque de código mientras se cumpla una condición. Comienzan con la palabra reservada while y llevan asociada una expresión lógica, de manera que mientras la evaluación de la expresión lógica sea TRUE se repite la ejecución del bloque de código que contiene.

while (<condición>) {
  <código>
}

La expresión lógica <condición> se evalúa antes de ejecutar el bloque de código y solo se ejecuta el <código> si el resultado de la evaluación es TRUE. Obsérvese que cuando el flujo de ejecución del programa llega al bucle while si la condición no es cierta, el código no se ejecuta ni tan siquiera una vez.

Diagrama de flujo de un bucle condicional

Ejemplo 4.8  

i <- 5
while (i >= 0) {
  print(i)
  i <- i - 1
}
[1] 5
[1] 4
[1] 3
[1] 2
[1] 1
[1] 0

4.2.3 La instrucción break

La instrucción break se utiliza para detener un bucle y salir de él, tanto en bucles iterativos como en bucles condicionales. Normalmente se suele utilizar esta instrucción cuando se cumple una determinada condición en bloque de código del bucle y se decide parar su ejecución y salir del bucle.

Ejemplo 4.9  

# Bucle que recorre los números enteros del -2 al 2 pero termina al llegar al 0.
for (i in -2:2) {
  if (i == 0) {
    break
  } 
  print(i)
}
[1] -2
[1] -1

4.2.4 La instrucción next

La instrucción next se utiliza para interrumpir la ejecución del bloque de código de un bucle, pero en lugar de salir del bucle pasa a la siguiente iteración. Si se trata de un bucle iterativo el iterador pasa al siguiente elemento de la secuencia de iteración y si se trata de un bucle condicional se pasa evaluar de nuevo la condición de repetición.

:::{#exm-continuacion-bucle-next}

# Bucle que recorre los enteros del 1 al 10 pero solo imprime los números pares.
for (i in 1:10) {
  if (i %% 2) {
    next
  }
  print(i)
}
[1] 2
[1] 4
[1] 6
[1] 8
[1] 10

4.3 Ejercicios

Ejercicio 4.1 Crear una función para calcular la media de un vector numérico y usarla para calcular la media del vector (1, 2, NA, 3, 4).

media <- function(x){
  suma <- 0
  n <- 0
  for (i in x){
    if (!is.na(i)){
      suma <- suma + i
      n <- n + 1
    }
  }
  return(suma / n)
}
media(c(1, 2, NA, 3, 4))
[1] 2.5

Ejercicio 4.2 Usar la función anterior para crear una función para calcular las medias de las columnas de un data frame numérico. La función debe devolver un vector con las medias de las columnas. Usarla para calcular el data frame formado por los vectores (1, 2, NA, 3, 4) y (-1, 0, -2, 0, NA).

medias <- function(df){
medias <- NULL
for (i in colnames(df)){
  medias <- c(medias, media(df[[i]]))
}
return(medias)
}

df <- data.frame(x = c(1, 2, NA, 3, 4), y = c(-1, 0, -2, 0, NA))
medias(df)
[1]  2.50 -0.75