====== 05 - Programación estructurada ====== La **programación estructurada** es un paradigma de programación donde un programa utiliza solo 3 estructuras básicas: secuencias, selecciones (condicionales) e iteraciones (bucles). Por ahora hemos estado utilizando solo secuencias, donde una instrucción se ejecutaba tras otra. En este tema vamos a ver las otras dos estructuras básicas. ===== Selección ===== Se ejecuta un conjunto de instrucciones en función del resultado de evaluar una condición (//true// o //false//). En Java existen dos estructuras básicas de selección: **if-else** y **switch**. ==== if-else ==== La sentencia **if** contiene una condición (o conjunto de condiciones) que se evalúa a //true// o //false//. En función del resultado se ejecutan o no las sentencias encerradas en el bloque de código siguiente: class SentenciaIf { public static void main(String[] args) { int number = 3; if(number<5) { System.out.println("El número es menor que 5"); } System.out.println("Fin del programa"); } } El número es menor que 5 Fin del programa En el ejemplo anterior, definimos una variable de tipo entero con valor 3. A continuación, usamos la sentencia //if// para preguntar si el número es menor que 5. Como en este caso se cumple la condición, se ejecuta el bloque de código encerrado entre llaves y muestra la frase //El número es menor que 5//. Si modificamos el valor de la variable con un número mayor que 5 y volvemos a ejecutar el programa, vemos que ya no muestra la frase (no se ejecutan las líneas de código de la sentencia //if//). ... int number = 7; ... Fin del programa A la hora de evaluar una condición, hay que poner especial atención con los límites. Por ejemplo, en el código anterior, si la variable es igual a 5 no se mostraría la frase //El número es menor que 5//, ya que estamos preguntando si la variable **es menor que 5**. Si quisiésemos que se ejecutara el código cuando la variable es **menor o igual a 5** deberíamos modificar el código: ... if(number<=5) { ... Muchos problemas típicos (sobre todo cuando se está aprendiendo a programar) vienen de no evaluar la condición adecuada. En el ejemplo anterior hemos evaluado una condición simple, pero podemos evaluar condiciones múltiples usando los operadores && (AND) y || (OR): if(number<5 && number>2) { System.out.println("El número es menor que 5 y mayor que 2"); } Las condiciones pueden ser todo lo complicadas que queramos, aunque, en general, siempre es mejor usar condiciones lo más simples posible para mejorar la legibilidad de nuestro código (por ejemplo usando funciones, como veremos más adelante). ¿Y si en nuestro primer ejemplo queremos que cuando el número sea mayor o igual a 5 se muestra la frase //El número es mayor o igual a 5//? Podríamos utilizar otra sentencia //if// a continuación de la primera para evaluar esa condición y mostrar la segunda frase: if(number<5) { System.out.println("El número es menor que 5"); } if(number >= 5) { System.out.println("El número es mayor o igual que 5"); } Si ejecutamos la aplicación cambiando el valor de la variables (por ejemplo, primero con //number=3// y después con //number=8//) vemos que funciona como toca. El problema es que no es todo lo eficiente que debería ser. En este caso, estamos evaluando dos veces la misma condición (o similar), con el gasto computacional que implica. Para estos casos, existe la sentencia **else**. Esta sentencia se utiliza después de un //if// para ejecutar un bloque de código en caso que la condición del //if// sea evaluada como //false//: if(number<5) { System.out.println("El número es menor que 5"); } else { System.out.println("El número es mayor o igual que 5"); } Que un programa funcione no quiere decir que sea eficiente. En el ejemplo anterior no tiene mucha importancia, ya que es una aplicación con muy pocas líneas de código y muy sencillas, pero cuando hacemos un programa con miles de líneas de código, la eficiencia es un parámetro muy importante a tener en cuenta. Podemos encadenar varias sentencias //if-else// para evaluar múltiples condiciones: if(number<5) { System.out.println("El número es menor que 5"); } else if (number < 8) { System.out.println("El número está en el rango [5, 8["); } else if (number <10){ System.out.println("El número está en el rango [8, 10["); } else { System.out.println("El número es mayor que 10"); } Aunque el código anterior es perfectamente válido, la mayoría de veces nos conviene más utilizar (sobre todo por legibilidad y sencillez del código) la sentencia que veremos a continuación: **switch**. ==== switch ==== La sentencia **switch** es equivalente a encadenar varias sentencias **if-else**, pero de forma más elegante. Su sintaxis clásica (antes de Java 17) es: switch (key) { case value: break; default: break; } La variable //key// es lo que vamos a evaluar. A continuación, vamos añadiendo bloques //case value// que comprobaran si //key// tiene ese valor. En caso afirmativo, se ejecutará el código de ese bloque y terminará la ejecución de la sentencia. Si //key// no tiene ningún valor de los anteriores, se ejecutará el código del bloque //default// (este bloque no es obligatorio, aunque normalmente está presente en la sentencia): Por ejemplo, podemos sustituir el siguiente código: if (number == 5) { System.out.println("El número es igual a 5"); } else if(number == 6){ System.out.println("El número es igual a 6"); } else if(number == 7) { System.out.println("El número es igual a 7"); } else { System.out.println("El número es mayor que 7"); } usando la sentencia //switch//: switch (number) { case 5: System.out.println("El número es igual a 5"); break; case 6: System.out.println("El número es igual a 6"); break; case 7: System.out.println("El número es igual a 7"); break; default: break; } } La sentencia **break** le indica al intérprete que debe salir del bloque //switch//, con lo que no debe ejecutar las siguientes líneas. Para entenderlo, elimina las sentencias //break// del código anterior y ejecútalo: switch (number) { case 5: System.out.println("El número es igual a 5"); case 6: System.out.println("El número es igual a 6"); case 7: System.out.println("El número es igual a 7"); } El número es igual a 5 El número es igual a 6 El número es igual a 7 Como ves, al no existir la sentencia //break// el código seguiría ejecutándose y mostraría todas las frases por pantalla. ¿Qué pasa si queremos que se ejecute el mismo código cuando nuestra variable sea igual a un conjunto de valores? Para ese caso, podemos aprovechar lo que hemos visto de la ausencia de sentencia //break// y agrupar varios valores: switch (number) { case 1: case 2: case 3: case 4: case 5: System.out.println("El número es menor o igual a 5"); break; case 6: System.out.println("El número es igual a 6"); break; default: System.out.println("El número es mayor que 6"); break; } La sentencia //switch// ha ido añadiendo mejoras en su código a lo largo de las versiones de Java. Por ejemplo, desde la versión 13 podemos devolver valores a través del //operador flecha// sin necesidad de utilizar //break//: switch (numero) { case 1 -> System.out.println("El número es 1"); case 2 -> System.out.println("El número es 2"); case 3 -> System.out.println("El número es 3"); case 4 -> System.out.println("El número es 4"); default -> System.out.println("El número es mayor que 4"); } ==== Operador ternario ==== El **operador ternario** se puede utilizar para simplificar las sentencias //if-else// básicas. Su sintaxis es la siguiente: variable = (condición)?valor_si_verdader:valor_si_falso; Por ejemplo, podemos sustituir el siguiente código: if (a<5) { frase = "El número es menor que 5"; } else { frase = "El número es mayor que 5"; } por este otro usando el operador ternario: frase = (a<5)? "El número es menor que 5": "El número es mayor que 5"; ===== Iteración ===== La **iteración** (o **bucles**) son bloques de código que se repiten mientras se cumpla una condición. Aunque cada lenguaje de programación puede tener sus propios bucles, existen 3 tipos que están presentes en casi todos ellos: **while**, **do-while** y **for**. ==== while ==== Este bucle evalúa primero una condición, y repite el código de su bloque mientras esa condición sea cierta: class SentenciaWhileDo { public static void main(String[] args) { int number = 1; while (number < 10) { System.out.println(number); number++; } } } 1 2 3 4 5 6 7 8 9 En este ejemplo, el bucle se ejecutará hasta que la variable //number// sea mayor o igual que 10. En cada iteración, comprueba si //number// es menor que 10, y si es así, ejecuta el bloque de código y vuelve a ejecutar la comprobación. Este ciclo terminar cuando la condición del bucle es //false//. La condición de salida del bucle es un punto crítico. Mira el ejemplo anteior y piensa que pasaría si no existiera la siguiente línea dentro del bucle: number++; Si no aumentáramos el valor de la variable //number// en cada iteración, la condición del bucle (//number < 10//) siempre sería //true//, con lo que el bucle no terminaría nunca. Esto se conoce como **bucle infinito**, y es primordial detectarlos, ya que puede hacer que nuestra aplicación consuma todos los recursos de la máquina donde se está ejecutando. ==== do-while ==== La sentencia **do-while** es prácticamente igual que la sentencia //while// con una diferencia importante: mientras que el código de la sentencia //do-while// se ejecuta siempre al menos una vez, el de la sentencia //while// no tiene porqué. Esto es debido a que la condición en la sentencia //while// se evalúa **antes de la ejecución del código**, mientras que el de la sentencia //do-while// se evalúa **despues**. Por lo demás, la sintaxis y el funcionamiento de ambos bucles son casi iguales. class SentenciaDoWhile { public static void main(String[] args) { int number = 1; do { System.out.println(number); number++; } while (number < 10); } } Para entender la diferencia entre ambos, ejecuta el siguiente código: number = 10; do { System.out.println("Bucle do-while: " + number); number++; } while(number < 10); number = 10; while (number < 10) { System.out.println("Bucle while: " + number); number++; } Bucle do-while: 10 Como ves, aunque la condición de salida de ambos bucles es la misma (//number// >= 10), el bucle //do-while// se ejecuta una vez, ya que la comprobación está al final del bucle. ==== for ==== En los bucles anteriores (//while// y //do-while//) no podemos saber, a priori, cuantas veces se van a ejecutar. Esa es la principal diferencia entre éstos y el bucle **for**. Su sintaxis báisca es la siguiente: class SentenciaFor { public static void main(String[] args) { for (int number = 1; number < 10; number++) { System.out.println(number); } } } 1 2 3 4 5 6 7 8 9 En este bucle, primero definimos una variable y le damos un valor incial (//int number = 1//), después escribimos la condición del bucle (//number < 10//) y, por último, le decimos al bucle cuanto tiene que incrementar (o decrementar) el valor de la variable en cada iteración (//number++//). Este tipo de bucles era muy utilizado para recorrer //arrays// (un conjunto de elementos del mismo tipo), aunque, con el auge de la **programación funcional** se utilizan cada vez menos (lo que no quiere que no sean muy útiles en según qué circunstancias). ===== La sentencias break y continue ===== Podemos forzar al bucle a terminar su ejecución con la sentencia //break//: public static void main(String[] args) { for(int i=0;i<10;i++) { System.out.println(i); if(i>4) { break; } } } 0 1 2 3 4 5 En el ejemplo anterior, salimos del bucle cuando la variable //i// es mayor que 4. Por otro lado, podemos indicarle al bucle que termine la iteración actual con la sentencia //continue//, con lo que no seguirá ejecutando el código restante y saltará a la siguiente iteración: for(int i=0;i<10;i++) { if(i==4) { continue; } System.out.println(i); } 0 1 2 3 5 6 7 8 9 En este ejemplo, si el valor de //i// es 4, usamos la sentencia //continue// para evitar que ejecute la sentencia //System.out.println()// y salte a la siguiente iteración (//i// = 5). ===== Bucles anidados ===== En los ejemplos anteriores hemos visto como usar bucles simples. Podemos crear estructuras más complejas anidando bucles (ejecutando bucles dentro de otros). Por ejemplo, podemos sacar todas las tablas de multiplicar del 1 al 9 anidando dos bucles //for//: for(int i=1; i<10; i++) { for(int j=1; j<10;j++) { System.out.println(i + "*" + j + "=" + (i*j)); } } Podemos anidar todos los bucles que queramos, aunque no debemos abusar de esta técnica, ya que añade complejidad al código (y a su legibilidad). ===== ¿Qué bucle elegir? ===== Aunque siempre se pueden resolver cualquier problema con cualquier tipo de bucle (aunque algunos con más complejidad), existen situaciones donde es más conveniente utilizar uno de los 3 tipos de bucles: * Cuando sabemos el número de repeticiones que tendrá el bucle, mejor usar //for//. * Cuando no sabemos el número de repeticiones que tendrá el bucle, pero queremos que se ejecute al menos una vez, mejor usar //do-while//. * Cuando no sabemos el número de repeticiones que tendrá el bucle y no hace falta que se ejecute al menos una vez, mejor usar //while//. ===== Ejercicios ===== **Ejercicio 1.a** Crea luna aplicación que pregunte la edad y muestre la frase "Puedes pasar" o "No puedes pasar" si es mayor de edad o no. Utiliza la sentencia //if-else//. **Ejericicio 1.b** Modifica el ejercicio anterior para usar solo una sentencia //if//, sin sentencia //else//. **Ejercicio 1.c** Modifica el primer ejercicio para utilizar el operador ternario. **Ejercicio 2.a** Escribe un programa que calcule el mayor de dos números introducidos por teclado. **Ejercicio 2.b** Usa el operador ternario para calcular el mayor de los dos números del ejercicio anterior. **Ejercicio 3.a** Haz un programa que calcule si un número introducido por teclado es par o impar usando sentencias //if-else//. **Ejercicio 3.b** Modifica el ejercicio anterior para calcular si el número es par o impar usando el operado ternario. Para ello, crea una variable //par// de tipo //boolean//. **Ejercicio 4.a** Escribe un programa que pida un color por teclado. Si el color es rojo la aplicación escribirá "Color de la sangre". Si es azul, "Color del cielo". Si es amarillo, "Color del sol". Si no es ninguno de ese color, se mostrará el texto "Lo siento, ese color es muy aburrido". **Ejercicio 4.b** Modifica el programa anterior para que la aplicación no distinga entre mayúsculas o minúsculas (si el usuario introduce //Rojo// funcionará igual que si introduce //rojo//). **Ejercicio 4.c** Modifica el programa anterior para que a la hora de evaluar el color, no tenga en cuenta los espacios en blanco del principio y el final. Es decir, si el usuario introduce " Rojo" la aplicación funcionará igual. **Ejercicio 5.a** Escribe un programa que muestre el factorial de un número introducido por pantalla usando un bucle //while//. **Ejercicio 5.b** Modifica el programa anterior utilizando un bucle //for// **Ejercicio 6.a** Escribe un programa que pida un número por pantalla. La salida será todos los números pares que hay entre 1 y ese número. Usa un bucle //while// **Ejercicio 6.b** Modifica el programa anterior usando un bucle //for// **Ejercicio 6.c** Modifica el programa anterior (puedes usar el bucle //while// o el //for//) utilizando la sentencia //continue// dentro del bucle. **Ejercicio 6.d** Haz que la salida del programa anterior se muestre como una cadena de números separados por coma. Ejemplo: Escribe un número:17 2, 4, 6, 8, 10, 12, 14, 16 **Ejercicio 7.a** Escribe un programa que, dado un número encuentre el primer múltiplo de éste (excluyendo el mismo número). No utilices la sentencia //break//. **Ejercicio 7.b** Modifica el programa anterior utilizando la sentencia //break// dentro del bucle. **Ejercicio 8.a** Escribe un programa que muestre la tabla de multiplicar de un número introducido por el usuario. **Ejercicio 8.b** Haz que el programa anterior muestre, además de la tabla de multiplicar, el sumatorio desde 1 hasta el resultado (es decir, si el resultado es 6, calculará 6+5+4+3+2+1). Ejemplo de salida: Escribe un número:3 3*1 = 3 Suma: 6 3*2 = 6 Suma: 21 3*3 = 9 Suma: 45 3*4 = 12 Suma: 78 3*5 = 15 Suma: 120 3*6 = 18 Suma: 171 3*7 = 21 Suma: 231 3*8 = 24 Suma: 300 3*9 = 27 Suma: 378 **Ejercicio 9a** Haz un programa que muestre un menú con 3 opciones. Cuando el usuario elija una opción, la aplicación mostrará por pantalla "Ha elegido la opción número_opcion". Ejemplo: 1.- Opción A 2.- Opción B 3.- Opción C -------------------- Opción:2 Ha elegido la opción2 **Ejercicio 9.b** Haz que el menú anterior tenga una última opción que sea "0.- salir". La aplicación no terminará hasta que el usuario escoja la opción 0.