Tema 9 Estructuras condicionales
En este tema vamos a estudiar las instrucciones condicionales. Estas permiten ejecutar un código u otro en función del resultado de evaluar una condición lógica.
9.1 Sentencia if
La instrucción if
permite ejecutar un conjunto de instrucciones, u otro conjunto de instrucciones alternativo, en función de una condición. Veamos un ejemplo:
dividendo <- 7
divisor <- 2
if (divisor != 0) {
cat(dividendo, "/", divisor, "=", dividendo / divisor, "\n")
} else {
print("No es posible dividir entre 0")
}
## 7 / 2 = 3.5
Observa la sintaxis. La sentencia comienza con la palabra reservada if
, seguida de una expresión lógica encerrada entre paréntesis. Después, encerrada entre llaves ({}
), viene el conjunto de instrucciones a ejecutar si el resultado de evaluar la expresión lógica es verdadero. La parte else
(en otro caso) incluye otro conjunto de instrucciones (encerradas entre llaves) que se ejecutan si la expresión lógica es falsa. Modifica el código previo para que divisor
valga 0 y comprueba cómo se ejecutan las instrucciones asociadas a la parte else
. Los conjuntos de instrucciones se escriben indentados (es decir, desplazados) a la derecha para facilitar la lectura de la instrucción, aunque sintácticamente no es obligatorio indentar estos conjuntos de instrucciones.
La parte else
es opcional, por lo que si no necesitamos ejecutar instrucciones si no se verifica la condición lógica podemos omitirla.
# Convierte la variable x a positiva
(x <- rnorm(1))
## [1] 0.9005119
if (x < 0) {
print("Cambio el signo")
x <- -x
}
x
## [1] 0.9005119
Si no existe parte else
y sólo queremos ejecutar una instrucción, no es necesario usar las llaves para delimitar las instrucciones:
# Convierte la variable x a positiva
(x <- rnorm(1))
## [1] 0.9418694
if (x < 0) x <- -x
x
## [1] 0.9418694
La condición lógica debe producir un valor escalar de tipo lógico. No tiene sentido que la condición lógica sea un vector de valores lógicos con más de un elemento. El siguiente código produce un error.
9.1.1 Sentencias if
anidadas
Las instrucciones asociadas a una sentencia if
pueden ser de cualquier tipo, incluido la propia instrucción if
. Vamos a ver un ejemplo con un código que calcula el máximo de tres valores, almacenados en las variables x
, y
y z
:
9.1.2 Selección múltiple con la sentencia if
El siguiente fragmento de código, que convierte una calificación numérica en una categórica, tiene la estructura lógica de una selección múltiple, en el sentido de que existen n conjuntos de instrucciones mutuamente excluyentes. Es decir, se dispone de n conjuntos de instrucciones de los que sólo se ejecutará (seleccionará) un conjunto.
(nota <- runif(1, min = 0, max = 10))
## [1] 9.289427
if (nota >= 9) {
notac <- "Sobresaliente"
} else {
if (nota >= 7) {
notac <- "Notable"
} else {
if (nota >= 5) {
notac <- "Aprobado"
} else {
notac <- "Suspenso"
}
}
}
notac
## [1] "Sobresaliente"
Para implementar una estructura lógica de este tipo el cuerpo de la parte else
de todas las instrucciones if
viene constituido por una única instrucción if
, lo que implica que no es necesario utilizar llaves para delimitar el cuerpo de la parte
else
. Esto posibilita el escribir la selección múltiple utilizando un formato alternativo como el mostrado a continuación.
(nota <- runif(1, min = 0, max = 10))
## [1] 5.497947
if (nota >= 9) {
notac <- "Sobresaliente"
} else if (nota >= 7) {
notac <- "Notable"
} else if (nota >= 5) {
notac <- "Aprobado"
} else {
notac <- "Suspenso"
}
notac
## [1] "Aprobado"
Este formato resulta más legible para muchas personas porque todas las expresiones lógicas y conjuntos de instrucciones aparecen al mismo nivel de indentación, reforzando la idea de que es una selección múltiple excluyente, es decir, en la que sólo se ejecuta un conjunto de instrucciones.
9.2 La función ifelse
La función ifelse
es una versión vectorizada de la instrucción if
. Tiene tres parámetros: un vector de valores lógicos y dos vectores de expresiones. Si los vectores de expresiones tienen menos elementos que el vector lógico, se reciclan a la longitud del vector lógico. Veamos un ejemplo:
La función ifelse
produce un vector del mismo tamaño que el vector de valores lógicos usado como primer parámetro. El funcionamiento es el siguiente: para los índices del vector lógico (primer argumento) con valores de verdadero se usa el elemento del mismo índice del primer vector de expresiones (en el ejemplo v
). En caso de que el valor sea falso se usa el elemento del mismo índice del segundo vector de expresiones (en el ejemplo -v
). Veamos un ejemplo en el que se recicla el segundo vector de expresiones:
9.3 La sentencia switch
(avanzado)
Esta sección es avanzada y su contenido no es evaluable.
La sentencia if
produce un resultado al ser ejecutada: el resultado de evaluar la última instrucción que ejecuta de sus conjuntos de instrucciones asociados. Por ejemplo, supongamos que queremos asignar a la variable signo el valor "positivo"
o "negativo"
en función del signo de una variable. Esto lo podemos implementar así:
(x <- rnorm(1))
## [1] 0.7067611
if (x >= 0) {
signo <- "positivo"
} else {
signo <- "negativo"
}
signo
## [1] "positivo"
Pero también podemos usar el hecho de que if
produce un resultado para escribir un código más corto:
Supongamos ahora que tenemos una sentencia if
múltiple, cuyas condiciones equivalen a ver si una expresión coincide con cierta cadena de caracteres. Por ejemplo:
operando1 <- 3
operando2 <- 4
operador <- readline("Operador (+, -, * o /: ")
resultado <- if (operador == '+') {
operando1 + operando2
} else if (operador == '-') {
operando1 - operando2
} else if (operador == '*') {
operando1 * operando2
} else if (operador == '/') {
operando1 / operando2
} else {
"operando no contemplado"
}
resultado
En estas circunstancias: una selección múltiple en la que las distintas condiciones se corresponden con ver si una expresión coincide con una cadena de caracteres, se puede usar la instrucción switch
:
operando1 <- 3
operando2 <- 4
operador <- readline("Operador (+, -, * o /: ")
resultado <- switch(operador,
'+' = operando1 + operando2,
'-' = operando1 - operando2,
'*' = operando1 * operando2,
'/' = operando1 / operando2,
"operando no contemplado"
)
resultado
Como puede observarse, el uso de switch
produce un código mucho más compacto. Dado que para poder emplear la sentencia switch
se requiere unas condiciones poco comunes no profundizaremos más en su uso. El lector interesado puede buscar más información sobre ella.
9.4 Ejercicios
Dadas las siguientes expresiones lógicas trata de calcular si producirán el valor
TRUE
oFALSE
al ser evaluadas. Evalúalas después y comprueba si has acertado. Para evaluarlas ten en cuenta la precedencia de los operadores lógicos:!
tiene la mayor precedencia, seguido de&&
y de||
.Escribe un programa que lea dos números y determine cuál de ellos es el mayor.
Realice un programa que lea un valor entero y determine si se trata de un número par o impar. Sugerencia: un número es par si el resto de dividirlo entre dos es cero.
Escribe un programa que lea del teclado un carácter e indique en la pantalla si el carácter es una vocal minúscula o no.
Escribe un programa que lea del teclado un carácter e indique en la pantalla si el carácter es una vocal minúscula, es una vocal mayúscula o no es una vocal.
Escribe un programa que solicite una edad e indique en la pantalla si la edad introducida está en el rango [18,25].
Implementa un programa que lea los coeficientes del siguiente sistema de ecuaciones e imprima los valores que son solución para x e y:
\[ \begin{array}{c} ax + by = c \\ dx + ey = f \end{array} \]
El sistema puede resolverse utilizando las siguientes fórmulas:
\[ x = \frac{ce - bf}{ae - bd} \] \[ y = \frac{af - cd}{ae - bd} \]
Hay que tener en cuenta aquellos casos que no tienen solución, esto ocurre cuando las rectas representadas por las ecuaciones son paralelas (la solución al sistema de ecuaciones es el punto donde se cortan las dos rectas).
Escribe un programa que calcule las soluciones de una ecuación de segundo grado de la forma \(ax^{2}+bx+c = 0\), teniendo en cuenta que:
\[ x = \frac{-b\pm \sqrt{b^{2} - 4ac}}{2a} \]
Si las soluciones son complejas no las calcules e indica un mensaje en la pantalla. También debes tener en cuenta que
a
puede valer 0, en cuyo caso debes resolver un sistema con una incógnita. Sia
yb
valen 0 no hay solución.Realice un programa que lea del teclado las longitudes de los tres lados de un triángulo y muestre en la pantalla qué tipo de triángulo es de acuerdo con la siguiente casuística (
a
denota la longitud del lado más largo yb
yc
denotan la longitud de los otros dos lados):- Si \(a \geq b + c\), no se trata de un triángulo
- Si \(a^{2} = b^{2} + c^{2}\), es un triángulo rectángulo
- Si \(a^{2} < b^{2} + c^{2}\), es un triángulo acutángulo
- Si \(a^{2} > b^{2} + c^{2}\), es un triángulo obtusángulo
Haz una versión del ejercicio anterior en el que se lean los 3 vértices del triángulo en lugar de la longitud de los lados.
Dado el siguiente data frame:
notas <- data.frame(nombre = paste("Alumno", 1:10), acertadas = sample(30, size = 10, replace = TRUE ) ) notas$falladas = 30 - notas$acertadas
Añádele un campo con la nota categórica, de forma que los alumnos que hayan acertado 15 o más respuestas tengan de nota
"pasa"
y los que tengan menos de 15 tengan de nota"no pasa"
. Usa la funciónifelse
para hacer el cálculo.Realiza un programa que solicite dos valores enteros que indican un rango (se puede introducir primero el menor y luego el mayor o a la inversa) y a continuación lea un tercer valor entero e indique si dicho valor pertenece o no al rango.
Implementa un programa que dada una fecha (día, mes y año) determine si es válida o no. Suponemos que la fecha es válida si el día es válido según el mes, el mes está en el intervalo (1, 12) y el año es positivo. Consideramos que un año es bisiesto cuando es múltiplo de 4.
9.5 Solución a los ejercicios
i <- 6
j <- 12
(2*i) <= j
## [1] TRUE
(2*i-1) < j
## [1] TRUE
(i > 0) && (i <= 10)
## [1] TRUE
(i > 25) || (i < 50 && j < 50)
## [1] TRUE
(i < 4) || (j > 5)
## [1] TRUE
!(i > 6)
## [1] TRUE
# Mayor de dos números
x = as.numeric(readline("Primer número: "))
y = as.numeric(readline("Segundo número: "))
if (x > y) {
cat("El mayor es el", x, "\n")
} else {
cat("El mayor es el", y, "\n")
}
# Comprobar si un número es par
x = as.integer(readline("Introduce un entero: "))
if (x %% 2 == 0) {
cat(x, "es par\n")
} else {
cat(x, "es impar\n")
}
# Comprobar si un carácter es una vocal minúscula
c <- readline("Introduce un carácter: ")
if (c == "a" || c == "e" || c == "i" || c == "o" || c == "u") {
cat(c, "es una vocal minúscula\n")
} else {
cat(c, "no es una vocal minúscula\n")
}
# En este ejercicio se pude usar el operador %in% para obtener
# un código más conciso
if (c %in% c("a", "e", "i", "o", "u")) {
cat(c, "es una vocal minúscula\n")
} else {
cat(c, "no es una vocal minúscula\n")
}
# Comprobar si un carácter es una vocal minúscula, mayúscula
# o no es una vocal
c <- readline("Introduce un carácter: ")
if (c == "a" || c == "e" || c == "i" || c == "o" || c == "u") {
cat(c, "es una vocal minúscula\n")
} else if (c == "A" || c == "E" || c == "I" || c == "O" || c == "U") {
cat(c, "es una vocal mayúscula\n")
} else {
cat(c, "no es una vocal\n")
}
# Versión usando el operador %in%
if (c %in% c("a", "e", "i", "o", "u")) {
cat(c, "es una vocal minúscula\n")
} else if (c %in% c("A", "E", "I", "O", "U")) {
cat(c, "es una vocal mayúscula\n")
} else {
cat(c, "no es una vocal minúscula\n")
}
# Comprobar si una edad está en el rango [18, 25]
edad <- as.integer(readline("Introduce una edad: "))
if (edad >= 18 && edad <= 25) {
cat(edad, "está en el rango [18, 25]\n")
} else {
cat(edad, "no está en el rango [18, 25]\n")
}
# Sistema de dos ecuaciones con dos incógnitas
a <- as.numeric(readline('Introduce el coeficiente a: '))
b <- as.numeric(readline('Introduce el coeficiente b: '))
c <- as.numeric(readline('Introduce el coeficiente c: '))
d <- as.numeric(readline('Introduce el coeficiente d: '))
e <- as.numeric(readline('Introduce el coeficiente e: '))
f <- as.numeric(readline('Introduce el coeficiente f: '))
denominador <- a*e - b*d
if (a == d && b == e && c == f) {
cat('Las rectas se solapan\n')
} else if (denominador == 0) {
cat('Las rectas son paralelas\n')
} else {
x <- (c*e - b*f) / denominador
y <- (a*f - c*d) / denominador
cat('Solución: (', x, ', ', y, ')\n', sep = "")
intervalo_x = c(x - 10, x + 10)
plot(intervalo_x, (c - a * intervalo_x) / b, type = "l", col = "green")
lines(intervalo_x, (f - d * intervalo_x) / e, type = "l", col = "blue")
points(x, y, col = "red", pch = 19)
}
# Raíces ecuación de segundo grado
a <- as.numeric(readline('Introduce coeficiente a: '))
b <- as.numeric(readline('Introduce coeficiente b: '))
c <- as.numeric(readline('Introduce coeficiente c: '))
if (a == 0) {
if (b == 0) {
print('No tiene solución')
} else {
x <- -c / b
cat('Sistema lineal. Solución', x, '\n')
intervalo_x <- c(x - 5, x + 5)
plot(intervalo_x, b*intervalo_x + c, type = "l", col = "blue")
points(x, 0, col = "red", pch = 19)
abline(h = 0, lty = "dashed")
}
} else if (b^2 - 4*a*c < 0) {
print("Las soluciones son complejas")
} else {
x1 <- (-b + sqrt(b^2 - 4*a*c)) / (2*a)
x2 <- (-b - sqrt(b^2 - 4*a*c)) / (2*a)
cat('Soluciones reales:', x1, "y", x2, "\n")
xmin <- min(c(x1, x2))
xmax <- max(c(x1, x2))
x <- seq(xmin - 10, xmax + 10, length.out = 100)
plot(x, a*x ^ 2 + b*x + c, type = "l", col = "blue")
points(x1, 0, col = "red", pch = 19)
points(x2, 0, col = "red", pch = 19)
abline(h = 0, lty = "dashed")
}
# Calcular tipo de triángulo según la longitud de sus lados
a <- as.numeric(readline("Longitud el lado más largo: "))
b <- as.numeric(readline("Longitud de otro lado: "))
c <- as.numeric(readline("Longitud de otro lado: "))
if (a > b + c) {
print('No es un triángulo')
} else if (a ^ 2 == b ^ 2 + c ^ 2) {
print('Triángulo rectángulo')
} else if (a ^ 2 < b ^ 2 + c ^ 2) {
print('Triángulo acutángulo')
} else {
print('Triángulo obtusángulo')
}
# Calcular tipo de triángulo dados sus vértices
x1 <- as.numeric(readline("Coordinada x punto 1: ")) # p1 = (x1, y1)
y1 <- as.numeric(readline("Coordinada y punto 1: "))
x2 <- as.numeric(readline("Coordinada x punto 2: ")) # p2 = (x2, y2)
y2 <- as.numeric(readline("Coordinada y punto 2: "))
x3 <- as.numeric(readline("Coordinada x punto 3: ")) # p3 = (x3, y3)
y3 <- as.numeric(readline("Coordinada y punto 3: "))
d <- c(sqrt((x1 - x2) ^2 + (y1 - y2) ^ 2),
sqrt((x2 - x3) ^2 + (y2 - y3) ^ 2),
sqrt((x3 - x1) ^2 + (y3 - y1) ^ 2)
)
a <- max(d)
resto <- d[-which.max(d)]
b <- resto[1]
c <- resto[2]
if (a > b + c) {
print('No es un triángulo')
} else if (a ^ 2 == b ^ 2 + c ^ 2) {
print('Triángulo rectángulo')
} else if (a ^ 2 < b ^ 2 + c ^ 2) {
print('Triángulo acutángulo')
} else {
print('Triángulo obtusángulo')
}
# Dibuja el triángulo
plot(c(x1, x2, x3, x1), c(y1, y2, y3, y1), type = "o", pch = 19)
# Añadir nota categórica
notas <- data.frame(nombre = paste("Alumno", 1:10),
acertadas = sample(30, size = 10, replace = TRUE)
)
notas$falladas = 30 - notas$acertadas
notas$pasa = ifelse(notas$acertadas >= 15, "pasa", "no pasa")
notas
## nombre acertadas falladas pasa
## 1 Alumno 1 12 18 no pasa
## 2 Alumno 2 29 1 pasa
## 3 Alumno 3 27 3 pasa
## 4 Alumno 4 15 15 pasa
## 5 Alumno 5 6 24 no pasa
## 6 Alumno 6 21 9 pasa
## 7 Alumno 7 27 3 pasa
## 8 Alumno 8 3 27 no pasa
## 9 Alumno 9 8 22 no pasa
## 10 Alumno 10 19 11 pasa
# Valor en rango
ei <- as.integer(readline('Extremo del rango: '))
es <- as.integer(readline('El otro extremo del rango: '))
if (es < ei) {
tmp <- ei
ei <- es
es <- tmp
}
x <- as.numeric(readline('Introduce valor: '))
if (x >= ei && x <= es) {
cat(x, " pertenece al interval [", ei, ", ", es, "]\n", sep = "")
} else {
cat(x, " no pertenece al interval [", ei, ", ", es, "]\n", sep = "")
}
# Día válido
dia <- as.integer(readline('Día: '))
mes <- as.integer(readline('Mes: '))
anio <- as.integer(readline('Año: '))
if (mes %in% c(1, 3, 5, 7, 8, 10, 12)) {
dia_maximo <- 31
} else if (mes %in% c(4, 6, 9, 11)) {
dia_maximo <- 30
} else if (mes == 2) {
if (anio %% 4 == 0) {
dia_maximo <- 29
} else {
dia_maximo <- 28
}
}
if (mes < 1 || mes > 12 || anio < 0 || dia < 1 || dia > dia_maximo) {
cat(dia, "/", mes, "/", anio, " es incorrecto\n", sep = "")
} else {
cat(dia, "/", mes, "/", anio, " es correcto\n", sep = "")
}