Fundamentos

Estructuras de control IF y ELSE

Básicos Definiciones Fundamentos

Demos un paso más en nuestro aprendizaje de la programación básica y empecemos con aspectos más importantes y que nos ayudarán en el flujo de instrucciones de nuestros programas. Estamos hablando de las estructuras de control y, concretamente, vamos a ver las estructuras de control IF y ELSE.

Para ver todo lo que ya hemos dejado atrás, accede al índice de contenidos, al igual que para ver todo lo que vendrá.

¿Qué son las estructuras de control?

Hasta ahora, hemos formado nuestros programas como una serie de líneas que se iban ejecutando una detrás de otra. El orden de ejecución es el mismo orden con el que hemos escrito el programa. Así, nuestro programa iniciaba su ejecución en la línea 1 y seguía hasta llegar a la última línea. Es decir, el flujo de ejecución de nuestros programas ha sido, hasta ahora, estrictamente secuencial.

Sin embargo, vamos a querer prácticamente siempre que nuestros programas sean capaces de decidir a partir de los datos que vayan encontrándose. Ésto lo pudimos intuir en artículos anteriores al hablar de los operadores de comparación y lógicos, pues esos operadores están indicados para tomar decisiones.

Así pues, las estructuras de control serán las instrucciones que le demos a nuestros programas que vayan a permitir alterar el flujo de ejecución. Nuestros programas serán capaces de, en base a una serie de normas lógicas, qué “camino” tomar e incluso repetir varias instrucciones más de una vez.

Estructuras de control hay tantas como un lenguaje de programación quiera definir, pero básicamente se reducen a un puñado muy concreto con el que podremos controlar nuestro código. Las primeras que vamos a ver van a ser las estructuras de control IF y ELSE, las cuales son estructuras condicionales, pues en base a una condición haremos una tarea u otra.

Nota
Podríamos traducir estas estructuras a SI y SI NO, pero en el mundo de la programación son tan habituales en sus formas en inglés que vamos a verlas directamente así y evitar traducciones que puedan llevar a errores.

Un programa de ejemplo

Para ir introduciendo estas estructuras vamos a realizar un programa de ejemplo y ver qué podemos encontrarlo y cómo podemos solucionarlo con ellas. Nuestro programa de ejemplo simplemente está diseñado para resolver una ecuación de primer grado. Vamos a verlo en pseudocódigo.

Para resolver una ecuación a * x + b = 0

a = 8.0
b = 2.0

x = -b / a
IMPRIMIR x

El resultado del programa sería -0.25

Hasta aquí vemos que el programa ha ido instrucción por instrucción. Guarda en la variable a el valor 8.0 (en decimal en previsión de que hay una división por ahí) y la variable b contendrá el valor 2.0. Como ya hemos despejado la x por nuestra cuenta, indicamos en nuestro programa que la variable x deberá tener asignado el valor de -b / a. Si queremos saber otras soluciones, cambiaríamos el valor de a y de b y ejecutaríamos.

Sin embargo, este programa tiene un punto débil.

Un error típico con divisiones

¿Qué sucedería si colocamos el valor 0 para la variable a? Llegaríamos a una división por 0 y ésto causaría un error, pues no existe ningún número que se pueda dividir por cero y nuestro programa no sabe qué hacer con ello.

Es ahí donde entra nuestra estructura de control condicional IF.

La sentencia condicional IF

Este IF, que sería traducido como SI, nos indicará si se debe ejecutar una serie de instrucciones u otras en base a si se cumple una condición o no. Vamos a colocarla primero en nuestro programa y luego la vemos más detenidamente.

IF (a !== 0) {
  x = -b / a
  IMPRIMIR x
}

Iniciamos el programa con nuestro IF. A continuación hemos colocado entre paréntesis (ver nota aclaratoria) la condición que queremos que se cumpla en ese IF. Es decir, en este caso tenemos que si a no es igual a 0, entonces procedamos a realizar el código que está entre las llaves (ver nota aclaratoria).

En definitiva, y con nuestro lenguaje natural, le estamos diciendo al programa que si el valor de a es distinto de 0, entonces que ejecute el código donde se resuelve la ecuación y la muestre por pantalla.

Nota
¿Por qué paréntesis y llaves? Es habitual en muchos lenguajes de programación el que la condición que debe cumplir la estructura de control se englobe entre paréntesis, y las instrucciones que debe realizar si se cumple se engloben entre llaves que se inician nada más terminar el paréntesis. Normalmente también se indenta (darle separación al inicio del texto) el código que va dentro del IF para que podamos leerlo mejor, aunque al programa normalmente le da igual.

Pero hay lenguajes de programación donde no es así y prescinden de los paréntesis, colocando al final de la condición dos puntos, por ejemplo, y de las llaves, controlando lo que debe ejecutar la condición en base a la propia indentación del código. Debido a que estamos realizando nuestros ejemplos en los demás artículos en PHP, vamos a utilizar la forma en la que allí se expresan.

Gracias a esta condición, cuando el valor de a sea 0, nuestro programa no ejecutará las líneas donde se resuelve la ecuación y, por tanto, no mostrará error.

Mostrando siempre información

Sin embargo, si ejecutamos el programa con ese valor de a igual a 0, veremos que no se nos muestra nada por pantalla. Nuestro IF no se ha cumplido al devolver a !== 0 un booleano falso y el programa no ha ejecutado nada más, pues no había nada a continuación. Vamos a arreglarlo primero de esta forma:

IF (a !== 0) {
  x = -b / a
  IMPRIMIR x
}

IF (a === 0) {
  IMPRIMIR 'La ecuación no tiene solución'
}

Es decir, que ahora el programa no ejecutaría las instrucciones que siguen al primer IF, pues la condición no se cumple, pero sí que ejecutaría la instrucción del segundo IF al cumplirse que a es igual a 0. Nuestro programa sólo nos mostraría por pantalla ese texto que hemos colocado.

Anidando estructuras de control

Gracias a nuestra estructura IF hemos podido controlar el error que aparecía con un valor 0 en el divisor indicándonos un mensaje de que la ecuación no tiene solución si tiene el valor 0. Sin embargo, ésto no es del todo cierto, pues si además del divisor también tenemos el dividendo con valor 0, entonces la ecuación tiene infinitas soluciones al ser una división 0 entre 0. Para solucionarlo, veamos el siguiente refinamiento:

IF (a !== 0) {
  x = -b / a
  IMPRIMIR x
}

IF (a === 0) {
  IF (b !== 0) {
    IMPRIMIR 'La ecuación no tiene solución'
  }

  IF (b === 0) {
    IMPRIMIR 'La ecuación tiene infinitas soluciones'
  }
}

Acabamos de anidar una estructura de control dentro de otra. Como podemos ver, si el valor de a es 0 nuestro programa no entraría por el primer IF sino por el segundo. Dentro de él se ha encontrado con otro IF, en este caso para saber si b es distinto de 0. Si lo es, entonces llegamos a que la ecuación no tiene solución. Pero si no lo es, nuestro programa acabaría entrando por el segundo IF que había dentro y nos mostrará que la ecuación tiene infinitas soluciones.

Anidar estructuras de control es otro uso básico en nuestros programas y lo veremos en gran cantidad de ocasiones de forma habitual.

Utilizando los operadores lógicos

Aprovechando que vimos en un anterior artículo los operadores lógicos, vamos a utilizarlos aquí y mostrar que un mismo programa se puede escribir de muchas formas distintas. Veámoslo.

IF (a !== 0) {
  x = -b / a
  IMPRIMIR x
}

IF (a === 0 AND b !== 0) {
  IMPRIMIR 'La ecuación no tiene solución'
}

IF (a === 0 AND b === 0) {
  IMPRIMIR 'La ecuación tiene infinitas soluciones'
}

Hemos partido por dos el segundo IF y ahora tenemos tres IF más generales, pero dos de ellos utilizando el operador lógico AND. Es sencillo ver lo que está haciendo sólo leyendo la condición. En el segundo IF tenemos que, si a vale 0 y b es distinto de 0, se imprima que no hay solución. Y en el tercer IF tenemos que si a vale 0 y b es igual a 0, se imprima que hay infinitas soluciones.

Es otra forma de expresar lo mismo con algunas de las herramientas que ya hemos visto. Dependerá, de nuevo, de la habilidad del programador elegir una u otra. En este caso concreto yo me inclino por la anidación, pues evitamos comparar dos veces si a es igual a 0 y, además, es más fácil de leer cara a nuestros ojos.

Pero aún podemos mejorar más la legibilidad del programa con la estructura de control ELSE.

La sentencia condicional ELSE

Traducida como SI NO, realiza el caso contrario a lo que realiza IF, de ahí que las estructuras de control IF y ELSE se vean en realidad como una sola más completa. Es decir, que lo que se englobe en las llaves de ELSE se ejecutará cuando no se cumpla la condición para IF. Se ve más claro con un ejemplo. De nuevo, modificaremos el ejemplo anterior para adaptarlo a este nueva estructura.

IF (a !== 0) {
  x = -b / a
  IMPRIMIR x
} ELSE {
  IF (b !== 0) {
    IMPRIMIR 'La ecuación no tiene solución'
  } ELSE {
    IMPRIMIR 'La ecuación tiene infinitas soluciones'
  }
}

Leamos poco a poco lo que el pseudocódigo quiere decir. En la primera línea comprobamos si a es distinto de 0. Si la condición es cierta, realizamos la división e imprimimos la solución, y si acabaría el programa, pues no se ejecutaría nada más. En la cuarta línea, justo nada más después de acabar el código de la sentencia IF al cerrar las llaves, colocamos nuestro ELSE que está indicando lo contrario a la condición IF. ¿Y qué es lo contrario de que a sea distinto de 0? Pues que a sea igual a 0. De esta forma iríamos por este camino si a fuera igual a 0. Y dentro de este ELSE tenemos lo mismo con el valor de b y utilizando también las sentencias IF y ELSE para imprimir uno u otro mensaje.

El programa queda así mucho más legible, con una secuencia más lógica y comprensible desde nuestro punto de vista. Será más fácil ver lo que hace y más fácil depurarlo si tuviéramos que realizarlo.

Vemos así que las estructuras de control IF y ELSE se combinan perfectamente para darle sentido a nuestros programas y poder controlar el flujo de qué instrucciones se ejecutan y cuáles no.

Un buen programa

Un buen programa es aquel que tiene en cuenta todas las posibilidades. Ésto, realmente, es siempre muy difícil y es habitual que existan errores que, de primeras, no se nos pasen por la cabeza. Saber tener en cuenta estos errores distinguirá a un buen programador del que se pasa horas depurando o, peor aún, del que presenta un programa que acaba teniendo errores.

Nuestro programa anterior aún tiene errores posibles. Por ejemplo, ¿qué sucedería si colocamos que alguno de los valores de a y b son cadenas de texto y no números? Nuestro programa daría error, pues está esperando números. Deberíamos incorporar una nueva condición que, inicialmente, comprobara que los valores introducidos son números correctos.

Es habitual que un programador que está empezando (y muchas veces también programadores veteranos) no se esperen recibir un tipo de dato distinto al que tenían pensado. En los ejemplos de este artículo siempre pensábamos en números, ya fueran enteros o con decimales. Pero un usuario puede equivocarse y pulsar una letra por error, o hacerlo incluso aposta. El programa fallaría y perderíamos credibilidad.

Así pues, hay que tener siempre en cuenta los tipos de datos a la hora de construir nuestros programas y utilizar sabiamente las estructuras de control IF y ELSE para evitar errores. Veremos más de estas estructuras de control en el próximo artículo.

Deja un comentario