PrInf07: Más sobre expresiones lógicas
| Práctica de Informática | |
|---|---|
| Más sobre expresiones lógicas | |
| Práctica anterior | Siguiente práctica |
| Este artículo es un guión de prácticas de Informática | |
Al usar la instrucción if hemos tenido un primer contacto con expresiones lógicas. Hasta el momento hemos realizado operaciones numéricas, tanto con escalares como con vectores y matrices, y hemos visto cómo mostrar texto. Pero en Octave UPM hay más tipos de información: uno de los más importantes es el tipo de datos lógico. Estas variables solo pueden tomar dos valores, verdadero o falso, y son las que usa la instrucción if para tomar una decisión y ejecutar o no una parte del código.
Contenido
1 Requisitos previos
Es recomendable para esta práctica consultar el siguiente vídeo:
También es importante haber realizado la práctica 6.
2 Comandos que se aprenderán en esta práctica
En esta práctica vamos a ver qué significan los siguientes comandos de MATLAB / Octave
| Operadores relacionales (<, >, <=, >=, ==, ~=) | Operador AND (&) | Operador OR (|) | Operador NOT (~) | xor |
| Operador AND perezoso (&&) | Operador OR perezoso (||) |
3 Contenido de la práctica
Si examinamos el espacio de trabajo de Octave UPM, habremos observado que hasta ahora hemos visto dos tipos de datos: números y texto. Los números aparecen como double en el espacio de trabajo, y los textos como char. Vamos a ver ahora un tercer tipo, logical, que ya hemos usado de manera inadvertida en la práctica anterior.
En la práctica anterior, veíamos que se podía mostrar un mensaje si la temperatura era superior a 30. Escribíamos
if c > 30
disp('Mostrar un mensaje');
endEste código tiene diferentes partes:
- La primera es la instrucción if, que termina con end. Esta instrucción acepta a continuación una condición. Si la condición es verdadera, ejecuta el cuerpo de la instrucción (muestra el mensaje en este caso), y si no, continúa en la línea que viene después del end.
- La condición es en este caso c > 30. Es una expresión lógica que devuelve verdadero o falso.
- El cuerpo del mensaje lo forman los comandos que se van a ejecutar si la condición es verdadera. En este caso solo hay un comando, pero puede tener cualquier comando y cualquier otra instrucción. Por ejemplo, se podría poner un if dentro de otro if; en una práctica veremos cuándo es útil hacer este tipo de anidamientos. Aunque no es obligatorio para que el programa sea sintácticamente correcto, es una buena práctica indentar el código, para que el cuerpo aparezca con un par de espacios de sangrado. La indentación permite distinguir muy claramente qué forma parte del cuerpo del if y que está fuera.
En esta práctica nos vamos a centrar en cómo escribir la condición. En el caso del ejemplo, la condición usa el operador >, mayor que, que es un operador relacional infijo. Un operador infijo es el que va entre dos elementos (por ejemplo, en 1 + 2 el símbolo + es un operador infijo). Es relacional porque nos devuelve un resultado acerca de la relación que existe entre los elementos. Este operador devuelve verdadero si el elemento de la izquierda es mayor que el de la derecha. Existen muchos otros operadores relacionales, que tienen sus reglas de precedencia y combinación de varios operadores. Hay también otro tipo de operadores lógicos que nos permiten construir condiciones más complejas.
3.1 Operadores relacionales
Vamos a intentar usar expresiones lógicas fuera de las instrucciones if, para aprender cómo operar con ellos y ser capaces de construir condiciones más complejas.
- Borra todas las variables del espacio de trabajo ejecutando el comando clear.
- Crea una variable de nombre c que tenga como valor 70. Ejecuta en la línea de comandos ¿En qué variable se ha guardado el resultado? ¿Cuál es su valor? ¿Y su tipo? (consulta el espacio de trabajo para averiguar el tipo).
c > 30
- Escribe ahora ¿Qué variables hay en el espacio de trabajo? ¿De qué tipo? ¿Podrías explicar qué ha hecho este trozo de código?
d = c > 100;
- Como vemos, es posible asignar valores lógicos a variables. En ocasiones nos puede venir bien crear directamente variables que sean verdadero o falso. Este tipo de variables se denominan flags (banderín). Su estado cambia cuando ocurre algo en un programa, y permite detectar en otra parte del programa que eso ha ocurrido. En una práctica futura veremos una aplicación práctica de este tipo de variables. Sin embargo, no se pueden crear de cualquier manera. Por ejemplo, ¿Es la variable a un flag? ¿Por qué?
a = 1;
- Compara el resultado de la variable a con esta variable b ¿Cuál es la diferencia entre las variables a y b?
b = true;
- Igual que existe true, tenemos el alias false para crear variables lógicas de contenido falso. Por ejemplo, Según el espacio de trabajo, ¿Cuál es el valor de c'?
c = false;
- Existen varios tipos de relaciones que podemos comparar usando operadores. Hemos visto >, también podríamos usar <. Por ejemplo, ¿cuánto vale la variable ans despúes de ejecutar este código? ¿Por qué?
x = 10; y = 11; x < y
- Existen también los operadores >= y <=. Por ejemplo, ¿qué devuelve el siguiente código? ¿Por qué?
x = 10; y = 11; x <= y
- ¿Y este código?
x = 11; y = 11; x <= y
- Existe también el operador == para comprobar si dos variables tienen el mismo valor. Por ejemplo ¿Qué devuelve este código? ¿Por qué?
x = 11; y = 11; x == y
- El operador == comprueba si dos variables tienen el mismo valor. Recuerda que tenemos una variable a de tipo numérico, y una variable b de tipo lógico. ¿Tienen el mismo valor? Comprobémoslo con ¿Cuál es el resultado? ¿Por qué?
a == b
- Veamos otro ejemplo ¿Cuál es el resultado? ¿Por qué?
x = 10; y = 11; x == y
- Existe otro operador que es justo el contrario que ==. Es el operador ~= que comprueba si dos elementos tienen un valor diferente. Para obtener el símbolo ~ puedes pulsar AltGr y luego el número 4, y después darle a la tecla espacio. Veamos un ejemplo ¿Cuál es el resultado? ¿Por qué?
x = 10; y = 11; x ~= y
- Si probamos las variables a y b con el operador ~=, ¿qué resultado obtendremos? ¿Cuál es el resultado? ¿Por qué?
a ~= b
3.2 Operadores lógicos
Los operadores relacionales pueden combinarse entre ellos igual que cualquier otro tipo de operador, como por ejemplo los operadores aritméticos. Pero además, existen otras operaciones de carácter lógico que pueden ayudarnos a construir condiciones más complejas. Estas operaciones lógicas son: AND, OR y NOT.
El operador lógico AND se representa por el símbolo &. Es un operador infijo que devuelve el valor verdadero si ambos elementos son verdaderos. Devuelve falso en cualquier otro caso. Esto se suele representar con tablas de verdad. Por ejemplo, si realizamos la operación a & b, el resultado puede ser el siguiente, según los valores de a y b
| a | b | a&b |
|---|---|---|
| F | F | F |
| F | V | F |
| V | F | F |
| V | V | V |
El operador lógico OR se representa por el símbolo |. Es un operador infijo que devuelve el valor verdadero si al menos uno de los dos lados es verdadero. Su tabla de verdad es
| a | b | a|b |
|---|---|---|
| F | F | F |
| F | V | V |
| V | F | V |
| V | V | V |
El operador NOT se representa por ~. Es un operador unario, es decir, se aplica a un único elemento. El operador NOT cambia el estado del elemento al que se aplica. Es decir:
| a | ~a |
|---|---|
| V | F |
| F | V |
Veamos un ejemplo. Estamos programando una máquina que dispensa abonos de transporte, y queremos emitir el tipo de abono adecuado según la edad. Estamos en la parte del programa que decide si emite un abono joven, para menores de 23 años. El programa tiene que comprobar que el dinero introducido es suficiente para pagar los 50 eurazos que vale el abono, y tiene que comprobar que la edad del usuario es la adecuada (menor de 23). Tenemos dos variables dinero y edad que contienen la cantidad de dinero que ha introducido el usuario y su edad. El trozo de código que se encargaría de esta comprobación podría ser el siguiente:
if (edad < 23) & (dinero >= 50)
disp('Imprimiendo abono joven...');
endEste código solo muestra el mensaje si la edad es menor que 23 y la cantidad de dinero es mayor o igual que 50. Hay que resaltar que hemos usado paréntesis para dejar clara el orden en el que se realizan las operaciones. Primero se evaluarán los contenidos de los paréntesis, dando verdadero o falso cada uno de ellos, y luego el operador & devolverá verdadero solo si ambos son verdaderos. Cuando estamos combinando varios operadores, sobre todo relacionales y lógicos, es mucho mejor usar paréntesis que confiar en la precedencia de operaciones para dejar claro qué operación ocurre. De esta manera el código será más sencillo de entender.
El operador OR también puede usarse para construir condiciones más complejas. Por ejemplo, un estudiante puede aprobar una asignatura si la nota media es mayor o igual que 5, o si la nota del examen final es mayor que 5. El código podría ser:
if (notaMedia >= 5) | (examenFinal >= 5)
disp('Aprobado');
end
El uso del operador NOT está recomendado cuando la condición se lea más claramente usando este operador, ya que siempre es posible construir el complementario de una condición usando otros operadores. Por ejemplo, un cliente no puede pasar si no es mayor de edad:
if ~(edad>=18)
disp('No puede pasar.');
else
disp('Puede pasar.');
end
| |
Tarea: | Modifica el programa anterior para que no use el operador NOT y no cambie el resultado de la ejecución |
Existe otro operador que no es tan habitual, que se denomina OR exclusivo. Este operador devuelve verdadero solo si uno de los dos elementos es verdadero. Si los dos son falso, o los dos son verdaderos, devuelve falso. No tiene representación infija en Octave UPM, pero tenemos la función xor para usar este operador. Su tabla de verdad es:
| a | b | xor(a,b) |
|---|---|---|
| F | F | F |
| F | V | V |
| V | F | V |
| V | V | F |
Su uso no es tan habitual como el resto de operadores.
3.3 Evaluación perezosa
Algunos operadores lógicos no necesitan evaluar todos los elementos para saber qué resultado van a devolver. Por ejemplo, si el primer elemento en el operador AND es falso, da igual el valor que tenga el segundo elemento, el resultado será falso. Sin embargo, el operador AND evalúa ambos lados antes de devolver un resultado.
Para evitar evaluaciones innecesarias, existe una versión más inteligente que realiza evaluación perezosa. En un lenguaje de programación, evaluación perezosa es una técnica que evita calcular términos que no van a influir en el resultado final.
Para usar estos operadores, simplemente tenemos que repetirlos. Por ejemplo, el operador AND inteligente y perezoso es '&&', y el operador OR inteligente y perezoso es '||'. Solo estos dos operadores tienen su versión con evaluación perezosa.
Aunque su sintaxis (la manera de escribirlo) es diferente, su semántica (el resultado que devuelven para unas entradas en particular) no cambia en absoluto. Por este motivo, es una buena idea siempre usar los operadores perezosos por defecto. En el peor de los casos no estaremos ganando nada, pero en el mejor de los casos nos evitaremos cálculos innecesarios. Además, algunos programas, sobre todo implementaciones de métodos numéricos, pueden ser más sencillos de escribir y de entender si aprovechamos la evaluación perezosa (lo veremos en una próxima práctica).
Si repetimos los ejemplos de arriba con evaluación perezosa, el ejemplo del operador AND quedará como sigue:
if (edad < 23) && (dinero >= 50)
disp('Imprimiendo abono joven...');
endEl comportamiento no cambia en absoluto, pero quizás esta versión sea más eficiente que la anterior.
El ejemplo del operador OR quedaría como sigue:
if (notaMedia >= 5) || (examenFinal >= 5)
disp('Aprobado');
endDe nuevo, el comportamiento no cambia, pero esta versión puede ser más eficiente que la anterior.
4 Ejercicio post-práctica
Los operadores relacionales se pueden combinar entre ellos, y formar expresiones más complejas. Sin embargo, no siempre obtenemos los resultados previstos. Queremos escribir una condición para comprobar si la variable x está entre 10 y 12. Para ello vamos a realizar un pequeño programa
x = input('Dame el valor de x: ');
if 10 < x < 12
disp('x está entre 10 y 12');
else
disp('x NO está entre 10 y 12');
endPrueba a introducir los siguientes valores de x: 7, 11 y 13. ¿Funciona correctamente el programa? ¿Cómo lo arreglarías?