Saltar a contenido

Combinando expresiones lógicas en estructuras condicionales

Veamos cómo podemos combinar expresiones lógicas en estructuras condicionales en Python para mejorar nuestro código.

Supongamos que tenemos el siguiente problema: queremos comparar dos números enteros, x e y, para saber si son distintos o si son iguales.

Analicemos el diagrama de flujo de este problema:

Diagrama de flujo que compara la igualdad entre dos números

Este diagrama de flujo es teórico para entender el funcionamiento, pero no es posible de ejecutar en ningún lenguaje de programación, puesto que no es posible mezclar los caminos de flujo verdadero como se muestra en el diagrama.

¿Cuál es la primera condición que se evalúa? Si x < y. Si la respuesta es verdadera entonces seguimos la flecha de flujo por la rama verdadera hasta obtener el resultado "x es distinto de y" y luego continuamos hasta el final del programa.

¿Pero qué pasa si x no es menor que y? ¿Qué pasa si x es mayor que y?

Si la respuesta a la condición anterior es falsa, entonces seguimos la flecha de flujo por la rama falsa y nos encontramos con una nueva condición: si x > y. Si la respuesta es verdadera entonces seguimos la flecha de flujo por la rama verdadera hasta obtener el mismo resultado de la condición anterior: "x es distinto de y" y luego continuamos hasta el final del programa, igual que en el caso anterior. Es decir, que podríamos reutilizar algunas de las mismas partes del diagrama para resolver nuestro problema. No necesitamos agregar flechas de flujo ni rombos de decisión ni rectángulos de procesos adicionales de manera innecesaria.

¿Qué pasa si x tampoco es mayor que y? ¿Qué pasa si x es igual a y?

Si la respuesta a la segunda condición también es falsa, entonces seguimos la flecha de flujo por la rama falsa y nos encontramos con el resultado "x es igual a y" y luego continuamos hasta el final del programa.

Cabe destacar que, en este caso, no necesitamos hacer una tercera pregunta, otro diamante, para saber si x es igual a y puesto que al no existir otra posible respuesta, siempre será verdadera. Podemos imprimir inmediatamente el resultado y luego detener el programa. En este caso, esta pregunta es redundante y no es necesaria. Identifica a la respuesta por descarte, la última opción posible.

Si analizamos lo que acabamos de describir, podemos observar que las dos preguntas, en caso de ser verdaderas, nos llevan al mismo resultado. Por lo tanto, podemos combinar estas dos preguntas en una sola pregunta que nos permita obtener el mismo resultado.

Al igual que como lo hacemos en español, en inglés o en cualquier otro idioma, en programación también podemos hacer varias preguntas al mismo tiempo combinando los resultados obtenidos de alguna manera lógica para obtener una respuesta más precisa.

¡Para recordar!

Al combinar expresiones lógicas en estructuras condicionales, podemos simplificar nuestro código y hacerlo más eficiente.

En lugar de hacer varias preguntas, podemos combinar estas preguntas en una sola pregunta que nos permita obtener el mismo resultado.

Esto nos permite escribir un código más limpio y más fácil de entender.

¿Cómo logramos hacer esto?

El operador lógico or

El operador lógico or se utiliza para combinar dos o más condiciones y determinar si una expresión compuesta es verdadera o falsa, dependiendo de si al menos una de las condiciones es verdadera.

Por ejemplo, modifiquemos el código anterior para saber si x es o no es igual a y:

comparar v4.py
x = int(input("¿Cuál es el valor de x? "))
y = int(input("¿Cuál es el valor de y? ")

if x < y or x > y:
    print("x es distinto de y")

else:
    print("x es igual a y")

Si modificamos el diagrama de flujo, veríamos algo como esto:

Diagrama de flujo que compara la desigualdad entre dos números con una condición compuesta

Este diagrama de flujo si puede ser convertido en código utilizando un lenguaje de programación, puesto que aquí no se mezcla ningún camino de flujo de manera incorrecta.

Ahora, con este nuevo diseño, revisemos los escenarios posibles:

Terminal (Entrada/Salida)
¿Cuál es el valor de x? 1
¿Cuál es el valor de y? 2

x es distinto de y

En este caso, hemos ingresado 1 para x y 2 para y. Como 1 es menor que 2, la condición x < y es verdadera (y ya no nos importa lo que resuelva x > y), por lo que se imprime "x es distinto de y".

Si ejecutamos el programa nuevamente, pero esta vez ingresamos 2 para x y 1 para y, obtendremos el siguiente resultado:

Terminal (Entrada/Salida)
¿Cuál es el valor de x? 2
¿Cuál es el valor de y? 1

x es distinto de y

En este caso, 2 es mayor que 1, por lo que la condición x > y es verdadera (y entonces descartamos el resultado falso que obtuvimos previamente con x < y), por lo que se imprime "x es distinto de y".

Finalmente, si ingresamos el mismo valor para x e y, obtendremos el siguiente resultado:

Terminal (Entrada/Salida)
¿Cuál es el valor de x? 1
¿Cuál es el valor de y? 1

x es igual a y

En este caso, 1 es igual a 1, por lo que la condición x < y or x > y resulta falta, producto de que ambas condiciones internas son falsas, por lo que se imprime "x es igual a y".

Si bien nuestro código es correcto, podemos mejorarlo aún más. ¿Cómo podríamos hacerlo? Aunque sea una pequeña mejoría. ¿Qué piensas?

Tómate un momento para pensar en cómo podríamos mejorar nuestro código y luego continúa en la siguiente sección para descubrir la respuesta.

El operador lógico and

El operador lógico and se utiliza para combinar dos o más condiciones y determinar si una expresión compuesta es verdadera o falsa, dependiendo de si todas las condiciones son verdaderas. Es decir, una conjunción de una, dos o más preguntas que quizás queramos hacer a la vez.

Veamos un ejemplo de ello. Vamos a crear un nuevo programa, llamado "calificacion.py"

calificacion.py
calificacion = int(input("Ingrese la calificacion del estudiante (1/100): "))

if calificacion >= 90 and calificacion <= 100:
    print("Nivel A")

elif calificacion >= 80 and calificacion < 90: # No es <= 90 porque ya se evaluó el caso de 90
    print("Nivel B")

elif calificacion >= 70 and calificacion < 80: # No es <= 80 porque ya se evaluó el caso de 80
    print("Nivel C")

elif calificacion >= 60 and calificacion < 70: # No es <= 70 porque ya se evaluó el caso de 70
    print("Nivel D")

else: # Caso por defecto
    print("Nivel F")

En este código, le pedimos al usuario que ingrese una calificación para un estudiante, que debe ser un número entre 1 y 100. Luego, utilizamos una estructura condicional para determinar la calificación correspondiente al rango en el que se encuentra la calificación ingresada.

Recordemos que la función input() siempre devuelve una cadena de texto, por lo que debemos convertir el valor ingresado a un número entero utilizando la función int() para poder compararlo con otros números enteros.

Si ejecutamos el programa y probamos con diferentes calificaciones, obtendremos los siguientes resultados:

Terminal (Entrada/Salida)
Ingrese la calificacion del estudiante (1/100): 100
Nivel A

Ingrese la calificacion del estudiante (1/100): 95
Nivel A

Ingrese la calificacion del estudiante (1/100): 89
Nivel B

Ingrese la calificacion del estudiante (1/100): 71
Nivel C

Ingrese la calificacion del estudiante (1/100): 39
Nivel F

En este caso, si la calificación ingresada es mayor o igual a 90 y menor o igual a 100, se imprime "Nivel A". Si la calificación está entre 80 y 89, se imprime "Nivel B". Si la calificación está entre 70 y 79, se imprime "Nivel C". Si la calificación está entre 60 y 69, se imprime "Nivel D". Y si la calificación es menor a 60, se imprime "Nivel F", el caso por defecto.

Encadenando operadores de comparación

Tenemos nuestro programa de calificaciones. Funciona bien, pero estaría mejor si lográsemos robustecer la lógica un poco más. No está mal. Es correcta. Pero, ¿Podemos reducir la probabilidad de errores, ahora o más adelante? ¿Podemos aumentar la legibilidad del código? ¿Podemos aumentar la eficiencia de ejecución? ¿Podemos conseguir que la computadora realice menos pasos y seguir obteniendo el mismo resultado?

En Python, podemos encadenar operadores de comparación para simplificar nuestras expresiones condicionales. Por ejemplo, en lugar de escribir calificacion >= 90 and calificacion <= 100, podemos escribir 90 <= calificacion <= 100. ¿Cómo? ¿De dónde sale esto? ¿Cómo funciona? ¿Es posible? ¡Sí! ¡Es posible! ¡Y es más simple! ¡Y es más eficiente! ¡Y es más legible! ¡Y es más elegante! ¡Y es más Python!

Analicemos como es el proceso de transformación:

  1. Partimos de nuestro código original:

    calificacion.py
    calificacion = int(input("Ingrese la calificacion del estudiante (1/100): "))
    
    if calificacion >= 90 and calificacion <= 100:
        print("Nivel A")
    
    elif calificacion >= 80 and calificacion < 90: # No es <= 90 porque ya se evaluó el caso de 90
        print("Nivel B")
    
    elif calificacion >= 70 and calificacion < 80: # No es <= 80 porque ya se evaluó el caso de 80
        print("Nivel C")
    
    elif calificacion >= 60 and calificacion < 70: # No es <= 70 porque ya se evaluó el caso de 70
        print("Nivel D")
    
    else: # Caso por defecto
        print("Nivel F")
    
  2. Vamor a invertir la lógica de la primera de las condiciones en cada if:

    calificacion.py
    calificacion = int(input("Ingrese la calificacion del estudiante (1/100): "))
    
    if  90 <= calificacion and calificacion <= 100: # Refactorizamos la primera condición
        print("Nivel A")
    
    elif  80 <= calificacion and calificacion < 90: # Refactorizamos la primera condición
        print("Nivel B")
    
    elif  70 <= calificacion and calificacion < 80: # Refactorizamos la primera condición
        print("Nivel C")
    
    elif  60 <= calificacion and calificacion < 70: # Refactorizamos la primera condición
        print("Nivel D")
    
    else: # Caso por defecto
        print("Nivel F")
    

    En este caso, hemos invertido la lógica de la primera condición en cada if para que la comparación sea 90 <= calificacion en lugar de calificacion >= 90. Esto nos permite encadenar los operadores de comparación de manera más eficiente y legible. Por ejemplo, en lugar de decir que "calificacion es mayor o igual que 90", decimos que "90 es menor o igual que calificación".

    ¡Para recordar!

    Esta inversión no es posible realizarse en todos los lenguajes de programación. De hecho, es inviable en la mayoría de ellos. Python es un lenguaje que permite esta flexibilidad y legibilidad en la escritura de código.

  3. Luego, simplificamos la expresión de comparación combinando las expresiones lógicas en cada caso. Esto tampoco es posible en todos los lenguajes de programación. Pero en Python, sí lo es. Y es una de las características que lo hacen tan atractivo para muchos programadores.

    calificacion.py
    calificacion = int(input("Ingrese la calificacion del estudiante (1/100): "))
    
    if 90 <= calificacion <= 100: # Refactorizamos la primera condición
        print("Nivel A")
    
    elif 80 <= calificacion < 90: # Refactorizamos la primera condición
        print("Nivel B")
    
    elif 70 <= calificacion < 80: # Refactorizamos la primera condición
        print("Nivel C")
    
    elif 60 <= calificacion < 70: # Refactorizamos la primera condición
        print("Nivel D")
    
    else: # Caso por defecto
        print("Nivel F")
    

    En este caso, hemos simplificado la expresión de comparación en cada if utilizando la forma 90 <= calificacion <= 100 en lugar de 90 <= calificacion and calificacion <= 100. Esto nos permite escribir un código más limpio y más fácil de entender.

¡Para recordar!

Al combinar expresiones lógicas en estructuras condicionales, podemos simplificar nuestro código y hacerlo más eficiente.

En lugar de hacer varias preguntas, podemos combinar estas preguntas en una sola pregunta que nos permita obtener el mismo resultado.

Esto nos permite escribir un código más limpio y más fácil de entender.

¿Qué más podemos hacer para mejorar nuestro código?

Reflexionando sobre la optimización de expresiones condicionales

Cada vez que el programa deba evaluar un nivel de calificación, es probable que no sea necesario realizar dos preguntas para poder tomar la decisión correcta. Pensemos lo siguiente:

Es necesario preguntarse ¿Es mayor o igual a 90 y menor que 100? ¿Es mayor que 80 y menor que 90? Y así sucesivamente...

Si repasamos la lógica, nos daremos cuenta de que, en nuestra primera condición compuesta, si la calificación es mayor o igual a 90 y menor o igual a 100, el estudiante obtiene una A. Pero también nos daremos cuenta de que si no es así, pero si, en la segunda condición, la calificación es mayor o igual a 80, el estudiante obtiene una B.

¿Por qué no evaluamos si la calificación es menor a 90 también?

Pues porque si llegamos a evaluar esta seguna condición es porque la primera condición, que evalúa una calificación de 90 o más, resultó falsa. Por lo tanto, si la calificación es mayor o igual a 80, pero no es mayor o igual a 90, entonces sabemos que está en el rango de 80 a 89, menor a 90.

Con este concepto, reformularíamos nuestra lógica de las expresiones condicionales de la siguiente manera:

calificacion.py
calificacion = int(input("Ingrese la calificacion del estudiante (1/100): "))

if 90 <= calificacion <= 100: 
    print("Nivel A")

elif calificacion >= 80: # Refactorizamos la condición completa
    print("Nivel B")

elif calificacion >= 70: # Refactorizamos la condición completa
    print("Nivel C")

elif calificacion >= 60: # Refactorizamos la condición completa
    print("Nivel D")

else: # Caso por defecto
    print("Nivel F")

En lugar de hacer dos preguntas cada vez, verificando los límites inferiores y superiores de cada rango de valores, estamos siendo un poco más inteligentes al preguntar si la calificación es mayor o igual que 90 y menor o igual que 100, obteniento "Nivel A"

Por el contrario, si la calificación es mayor que 80, obtiene "Nivel B". Pero, ¿Por qué no puede ser "Nivel A"? Porque debido a la lógica if… elif, ya hemos verificado previamente si el puntaje del estudiante es mayor que 90, siendo falsa la respuesta. Entonces, implícitamente sabes que está en algún lugar en el rango de 80 a 89; de lo contrario, sabes que está en el rango de 70 a 79, o está en el rango siguiente hacia abajo, sucesivamente.

Esta es una optimización menor que nos permite hacer menos preguntas. Pero nuevamente, podemos decir que esto nos permite simplificar la lógica de las expresiones condicionales y hacer que el código sea un poco más legible, ciertamente más conciso y, con suerte, más mantenible a largo plazo.