04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en...

24
4-1 Módulo 4: ESTRUCTURAS DE CONTROL: BIFURCACIONES Y BUCLES

Transcript of 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en...

Page 1: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

4-1

Módulo 4:

ESTRUCTURAS DE CONTROL: BIFURCACIONES Y BUCLES

Page 2: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-2

SUMARIO

ESTRUCTURAS DE CONTROL: BIFURCACIONES Y BUCLES _______________________________________________________ 1

SUMARIO _________________________________________________________________2 4.1 BIFURCACIONES Y BUCLES ________________________________________4

4.1.1 Concepto de bloque______________________________________________________4 4.2 SENTENCIAS CONDICIONALES _____________________________________4

4.2.1 La sentencia if _________________________________________________________5 4.2.2 La sentencia if-else ___________________________________________________6 4.2.3 La sentencia if-else anidada____________________________________________8 4.2.4 La sentencia switch ____________________________________________________9

4.3 SENTENCIAS REPETITIVAS________________________________________11 4.3.1 La sentencia while_____________________________________________________11 4.3.1.1 Condición de salida de la sentencia while______________________14 4.3.1.2 Centinela en la condición de salida de la sentencia while ____16

4.3.2 La sentencia do-while _________________________________________________17 4.3.2.1 El control de datos mediante la sentencia do-while ____________18 4.3.2.2 Un ejemplo de la sentencia do-while ____________________________19

4.3.3 La sentencia for _______________________________________________________21 4.3.3.1 Un ejemplo de la sentencia for con caracteres _________________22 4.3.3.2 Otro ejemplo de la sentencia for con caracteres _______________23

Page 3: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-3

Page 4: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-4

4.1 BIFURCACIONES Y BUCLES Hasta ahora, y así seguiremos aún en los siguientes módulos, estamos estudiando Java como si se tratara de un lenguaje secuencial en vez de un lenguaje orientado a objetos. En los lenguajes secuenciales se ejecutan las sentencias sucesivamente una tras otra, y mediante las estructuras de control se determina el flujo de ejecución. A veces interesa que una serie de sentencias se ejecuten más de una vez, en otras ocasiones interesa que parte de las sentencias se ejecuten sólo si se dan ciertos valores en algunas variables. En definitiva, las sentencias de control guían el flujo de ejecución de los métodos y de los programas. Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos permiten controlar las bifurcaciones, y, por otra las sentencias repetitivas que nos permiten construir bucles.

4.1.1 Concepto de bloque Tanto las sentencias condicionales como las sentencias repetitivas suelen englobar varias sentencias simples. Para que un conjunto de sentencias simples se comporten como un sentencia compuesta, basta con encerrar las instrucciones simples entre un par llaves { }. El alumno se ha dado cuenta que hasta la fecha ya se están utilizando pares de llaves que delimitan zonas del programa. El concepto de bloque volverá a presentarse cuando estudiemos el ámbito de las variables, pero eso será un poco más adelante cuando comencemos a escribir nuestros propios métodos.

4.2 SENTENCIAS CONDICIONALES Las sentencias condicionales, las bifurcaciones, nos permiten ejecutar una acción elegida de entre un conjunto de acciones posibles. La decisión de qué acción se ejecutará está supeditada al valor de una expresión lógica o relacional. En esencia sólo existen dos bifurcaciones diferentes: if y switch. De la primera de las cuales existen diferentes modelos, para que sea más fácil la asimilación las presentaremos separadamente:

1. La sentencia if 2. La sentencia if else 3. La sentencia if else if 4. La sentencia switch

sentencia_A { sentencia_B1; sentencia_B2; sentencia_B3; ... } sentencia_C

sentencia_A sentencia_B sentencia_C

Las sentencias comprendidas entre lasllaves se comportan como si fueran unasentencia compuesta sentencia_B.

Page 5: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-5

4.2.1 La sentencia if La lógica de la sentencia if sigue este esquema: Puesto que la sentencia if es sumamente fácil no insistiremos dando más explicaciones y nos limitaremos a mostrar un programa que la emplea. El programa se llama EsParGrande y su objetivo es localizar los números pares y mayores que una determinada cantidad: En el programa EsParGrande aparece la sentencia for que sirve para repetir una serie de acciones, no es la primera vez que la vemos y ya la estudiaremos más adelante, de momento nos conformaremos con saber que para este caso repite cinco veces lo hay definido en su bloque. El valor de referencia se calcula fuera del for, y en cada repetición se obtiene un valor para numero, si ese valor es par y además es mayor que el contenido de referencia se escriben en pantalla los mensajes. El operador % En los libros anglosajones suele recibir el nombre de módulo y realmente devuelve el resto de la división entera. Se aplica con dos operandos enteros y el resultado también es un entero. Por ejemplo en la sentencia int resto=2%3; tenemos que resto valdrá 1.

sentencias;

sentencia_A;

sentencia_C;

sentencia_B

condicion SI

NO

sentencia_A; if (condicion) { sentencias; } sentencia_C;

class EsParGrande { public static void main(String[] args) { int referencia, numero; referencia = (int) (Math.random()*100); System.out.println("referencia -> " + referencia); for (int k=1; k<=5; k++) { numero = (int) (Math.random()*100); if ((numero % 2 == 0) && (numero > referencia)) { System.out.print("\nk=" + k); System.out.println("\tnumero -> " + numero); } } } }

Si el contenido de numero es par y además mayor que el contenido de referencia, escribir en pantalla. Si no, no hacer nada.

Page 6: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-6

Téngase en cuenta que la condición de entrada de la estructura if del programa EsParGrande es doble. Realmente son dos if-s anidados que cuando ambos se evalúen a true presentarán los mensajes. En estos casos de sentencias condicionales anidadas una dentro de otra, es mucho más recomendable acostumbrarse a utilizar una única sentencia if cuya condición compuesta contemple ambos aspectos de la decisión. Es decir, por claridad es mejor utilizar el esquema del programa EsParGrande cuyo flujo es:

4.2.2 La sentencia if-else La sentencia if-else se utiliza para elegir una opción entre dos que son contrarias entre sí, su lógica sigue este esquema:

sentencias;

sentencia_A;

sentencia_C;

sentencia_B;

condicion1 SI

NO

condicion2 SI NO

sentencia_A; if (condicion1) if (condicion2) { sentencias; } sentencia_C;

sentencia_A;

sentencia_B;

condicion1 &&

condicion2 sentencias;

sentencia_C;

SI

NO

sentencia_A; if (condicion1) && (condicion2) { sentencias; } sentencia_C;

sentencias_2;

sentencia_A;

sentencia_C; sentencia_B;

condicion NO

sentencias_1;

SI

sentencia_A; if (condicion) { sentencias_1; } else { sentencias_2; } sentencia_C;

Page 7: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-7

¿Qué ocurre si pretendemos dividir una cantidad por cero?, pues que en tiempo de ejecución se produce un error que hace detenerse al programa. No es la única manera de controlar los errores de ejecución, pero el programa ElCociente controla que el divisor no sea cero antes de ejecutar la operación donde se calcula el cociente:

class ElCociente { public static void main(String[] args) { int cociente, dividendo, divisor; for (int k=0; k<=9; k++) { dividendo = (int) (Math.random()*10); divisor = (int) (Math.random()*10); if (divisor == 0) { System.out.print("k=" + k + "\t\t"); System.out.print(dividendo + "/" + divisor); System.out.println(" = INFINITO"); } else { System.out.print("k=" + k + "\t\t"); cociente = dividendo / divisor; System.out.print(dividendo + "/" + divisor); System.out.println(" = " + cociente); } } } }

La condición if del programa ElCociente evita que para k=2 y k=7 (tercera y octava iteración del for) se calcule el cociente. Si eliminamos del programa ElCociente ese control podemos obtener una salida como la de abajo, donde vemos que el programa ha finalizado con un error en tiempo de ejecución, pues en la cuarta iteración (k=3) el divisor ha tomado el valor de cero:

¿Qué sucede si sequitan los paréntesisrojos?, ¿habrá error?

Esto es una excepción pero al no estar activo el código que la maneja se va propagando hasta llegar al método main() con lo que la ejecución del programa se deten-drá

Page 8: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-8

4.2.3 La sentencia if-else anidada La sentencia if-else se puede encadenar en varias estructuras anidadas unas dentro de otras, cuando una condición no se cumple da paso a la zona else que a su vez contiene un if. Se utiliza cuando hay que modelar una estructura de decisión entre varias alternativas. Hemos planteado dos programas que funcionan de forma similar, pero en uno de ellos el llamado NotasIfElseAnidadosEnOrden las condiciones son tales que si se altera el orden entre ellas el programa funciona dando resultados incorrectos, en el otro programa que se llama NotasIfElseAnidados el orden de las condiciones no influye en el resultado pues dichas condiciones determinan rangos. En el programa NotasIfElseAnidadosEnOrden si se cumple una condición ejecuta el mensaje asociado y sale del conjunto de if-else anidados. Por ejemplo en la salida que se muestra a continuación se han ido saltando posibilidades hasta llegar a la de notable, pero no se continúa presentando el mensaje de sobresaliente:

Consideramos que es mejor programar la misma situación tal y como se hace en el programa NotasIfElseAnidados:

class NotasIfElseAnidadosEnOrden { public static void main(String[] args) { double nota; nota = Math.random()*10; System.out.println("El valor de la nota es: " + nota); System.out.print(nota + " -----> "); /* el orden de las condiciones esta estrictamente fijado para el buen funcionamiento del programa */ if (nota < 3.0) System.out.println("Muy deficiente"); else if (nota < 5.0) System.out.println("Insuficiente"); else if (nota < 7.0) System.out.println("Bien"); else if (nota < 9.0) System.out.println("Notable"); else if (nota < 10.0) System.out.println("Sobresaliente"); else System.out.println("Dato incorrecto"); } }

Page 9: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-9

4.2.4 La sentencia switch Hemos visto que la sentencia if-else encadenada nos permite seleccionar una posibilidad entre múltiples alternativas, pero también se ha apreciado que su estructura es un tanto complicada. Cuando se trata de seleccionar una posibilidad entre múltiples alternativas y además el criterio de selección se debe al contenido de una variable simple la sentencia switch es muy eficiente. La variable de control bajo la cual se determina la elección en la sentencia switch puede ser, por ejemplo, del tipo int o del tipo char pero no puede del tipo double. Esto es así pues las diversas posibilidades contempladas en la sentencia switch deben ser enumeradas en ella, lo cual no es lógico para valores reales. L a sentencia switch responde a este esquema:

class NotasIfElseAnidados { public static void main(String[] args) { double nota; nota = Math.random()*10; System.out.println("El valor de la nota es: " + nota); System.out.print(nota + " -----> "); if (nota < 3.0) System.out.println("Muy deficiente"); else if ((nota >= 3.0) && (nota < 5.0)) System.out.println("Insuficiente"); else if ((nota >= 3.0) && (nota < 7.0)) System.out.println("Bien"); else if ((nota >= 7.0) && (nota < 9.0)) System.out.println("Notable"); else if (nota >= 9.0) System.out.println("Sobresaliente"); else System.out.println("Dato incorrecto"); } }

default case case case

sentencia_A;

selector

sentencia_B3; sentencia_B1; sentencia_B2; sentencia_B4;

sentencia_C; sentencia_B;

(opcional)

Page 10: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-10

Supuesto que a las notas les corresponden valores numéricos enteros, escribamos el programa NotasEnterasSwitch que hace uso de una estructura switch: class NotasEnterasSwitch { public static void main(String[] args) { int nota; nota = (int) (Math.random()*11); System.out.println("El valor de la nota es: " + nota); System.out.print(nota + " -----> "); switch (nota) { case 0: case 1: case 2: System.out.println("Muy deficiente"); break; case 3: case 4: System.out.println("Insuficiente"); break; case 5: System.out.println("Suficiente"); break; case 6: System.out.println("Bien"); break; case 7: case 8: System.out.println("Notable"); break; case 9: System.out.println("Sobresaliente"); break; case 10: System.out.println("Matricula"); break; default: System.out.println("Dato incorrecto"); } } } Una posible salida de la ejecución del programa NotasEnterasSwitch podría ser esta, donde una parte del mensaje (la señalada en rojo) se escribe desde dentro de la sentencia switch:

Si la nota hubiera sido un 8 se obtendría el mismo literal Notable

¿Qué sucede si sequitan los enunciadosbreak?, ¿habrá error?

Page 11: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-11

4.3 SENTENCIAS REPETITIVAS Tal y como se ha visto en las sentencias condicionales, también llamadas alternativas, se tiene la posibilidad de ejecutar una o más sentencias en función de los valores de una expresión condicional. En cualquier caso, independientemente de que la decisión fuera ejecutar o no ejecutar el bloque de sentencias, en las sentencias alternativas el flujo del programa siempre avanza. Por ejemplo, tomando la sentencia if: Sin embargo, en las sentencias repetitivas se debe pasar por la misma sentencia (o bloque de sentencias) más de una vez, por lo que el flujo del programa debe tener la posibilidad tanto de avanzar como de retroceder; retroceder para poder efectuar la repetición, avanzar para no quedarse en un bucle infinito. Todo bucle de cualquier programa se puede representar esquemáticamente de la siguiente manera: A continuación examinaremos estas sentencias repetitivas:

1. La sentencia while 2. La sentencia do- while 3. La sentencia for

4.3.1 La sentencia while Supongamos que deseamos calcular el factorial de un número entero, con lo aprendido hasta la fecha plantearíamos el programa de la siguiente manera (lo aprendido y lo visto difieren pues ya hemos utilizado la sentencia for que bien podría suponer una solución más completa como más adelante comprobaremos). Pero volvamos al planteamiento inicial, se fija un número entero que es dato y se calcula su factorial aplicando esta fórmula matemática:

5! = 5 * 4 * 3 * 2 * 1

C

B

A Sentencias;

sentencia_A;

sentencia_C;

sentencia_B;

condicion SI

NO

sentencias;

sentencia_A;

sentencia_C;

sentencia_B

condicion SI

NO

sentencia_A; if (condicion) { sentencias; } sentencia_C; C

B

A

Terminología

Cada paso a través del bucle de repeti-ción se dice que es una iteración.

Page 12: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-12

El programa FactorialSinBucle que mostramos calcula el factorial de 5 pero su algoritmo es tal que habría que cambiar el programa para calcular el factorial de otro número: public class FactorialSinBucle { public static void main(String[] args) { final int entero = 5; int factorial; factorial = 5*4*3*2*1; System.out.println(entero + "! = " + factorial); } } Reconsideremos el problema de forma que sea posible calcular el factorial de un número entero cualquiera fijado dinámicamente durante la ejecución del programa. Para ello el algoritmo más sencillo es este: Supuesto que el número dato fuera 4 (obtenido aleatoriamente), asignamos dicho valor a la variable entero y efectuamos cuatro iteraciones: entero ← 4 La variable auxiliar actual va recogiendo las soluciones parciales que se acumulan en cada iteración, inicialmente actual vale 1: 1 * 2 * 3 * 4 La variable auxiliar contador se encarga de asegurar que sean 4 las iteraciones. Parcial al final de la iteación 1: 1 ← actual * 1 Parcial al final de la iteación 2: 2 ← actual * 2 Parcial al final de la iteación 3: 6 ← actual * 3 Parcial al final de la iteación 4: 24 ← actual * 4 Ese algoritmo que calcula 4! = 24 considera dos conceptos interrelacionados. Por una parte, existe un contador que determina el número de iteraciones a completar, puesto que será un número entero y variable se utilizará el tipo int para su declaración eligiendo como identificador la etiqueta contador que describe perfectamente su cometido. El segundo concepto nuclear del algoritmo es que la solución final se va construyendo paso a paso, es decir, en cada proceso repetitivo nos vamos acercando a la solución final. Lo cual supone que se deben guardar las soluciones parciales en alguna variable auxiliar, que hemos denominado actual y puesto que se tratará de un número entero grande la hemos elegido de tipo long. En resumen, las sentencias a repetir serán: actual = actual * contador; contador = contador + 1;

actual 24 actual 6

actual 2 actual 1

contador

Page 13: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-13

En esos dos cálculos los nuevos valores se determinan en función de los previos, lo cual significa que en la primera iteración deberemos necesariamente asegurar que el contenido de la variable actual y el contenido de la variable contador son los correctos. Estas inicializaciones son absolutamente obligatorias: contador = 1; actual = 1; Para finalizar la presentación del programa FactorialIterativo es preciso añadir que la lógica de la sentencia while es tal que se repite su bloque de mandatos mientras sea cierta la condición de salida: Con estas explicaciones debería ser suficiente para entender el funcionamiento del programa FactorialIterativo, incluso queda resuelta la situación particular del factorial de cero cuya solución matemática por convenio es 1: public class FactorialIterativo { public static void main(String[] args) { int entero; int contador; long actual; entero = (int) (Math.random()*20); System.out.println("entero -----> " + entero); contador = 1; actual = 1; while (contador <= entero) { actual = actual * contador; contador = contador + 1; } System.out.println(entero + "! = " + actual); } }

sentencias;

sentencia_A;

sentencia_C;

sentencia_B;

condicion SI

NO

sentencia_A; while (condicion) { sentencias; } sentencia_C;

Page 14: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-14

4.3.1.1 Condición de salida de la sentencia while Al programa FactorialIterativo le corresponde una condición de salida muy sencilla, de hecho es el tipo de condición que permite resolver el problema mediante la sentencia for que es muy compacta y fácil de utilizar. Pero planteemos otro ejemplo que necesariamente debe ser resuelto mediante una sentencia while, se trata de hallar el valor de la serie:

1/12 - 1/22 + 1/32 - 1/42 + 1/52 - 1/62 + ...

Debemos calcular el sumatorio de un número indeterminado de sumandos, por lo que nos deberán aportar más datos. Si el dato fuera “sumar los x primeros sumandos de la serie” nos encontraríamos en la misma situación que en el programa FactorialIterativo donde la condición de salida sería algo así como while (contador <= x) donde x es conocido. Pero si el dato para fijar la cantidad de sumandos fuera del tipo “sumar términos hasta que el último de ellos sea menor que la cantidad x”. En este caso el número de sumandos no es conocido explícitamente si no que de forma indirecta, programémoslo escribiendo la clase SerieWhile que responde al enunciado: “calcular el valor de la serie mientras el valor absoluto de los sumandos sea mayor que 0.05“. public class SerieWhile { public static void main(String[] args) { int contador; int signo; double sumando; double solucion; final double precision = 0.05; contador = 1; sumando = 1; signo = 1; solucion = 0; while ((Math.abs(sumando) > precision)) { sumando = signo * (1.0 / (contador*contador)); solucion = solucion + sumando; System.out.println("\nsumando" + contador + " = " + sumando); System.out.println("serie = " + solucion); contador = contador + 1; signo = signo * (-1); } solucion = solucion - sumando; System.out.println("-------------------------------"); System.out.println("\nSerie = " + solucion); } } En la quinta iteración se cumple la condición de salida del bucle, y como se ha añadido su correspondiente sumando a la solución, es necesario restárselo antes de la presentación.

Page 15: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-15

Atendiendo al programa SerieWhile se propone realizar los siguientes cambios y recoger en la tabla la consecuencia observada (para eliminar o desactivar sentencias basta con comentarlas):

Cambio Consecuencia

Eliminar solucion = 0;

Eliminar sumando = 1;

Eliminar signo = 1;

Eliminar contador = 1;

Eliminar la sentencia del bucle: contador = contador + 1;

Eliminar del bucle las sentencias: System.out.println

Eliminar la sentencia del bucle: signo = signo * (-1);

Page 16: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-16

Como el lector ya habrá advertido las variables signo y contador están muy relacionadas entre sí (cuando el contador toma valores impares el signo es positivo, y cuando los valores del contador son pares el signo es negativo). A continuación se escribe el mismo programa SerieWhile al que llamamos SerieWhile1 donde se elimina la variable innecesaria y las salidas de pantalla internas al bucle: public class SerieWhile1 { public static void main(String[] args) { int contador; double sumando; double solucion; final double precision = 0.05; contador = 1; sumando = 1; solucion = 0; while ((Math.abs(sumando) > precision)) { sumando = 1.0 / (contador*contador); if (contador % 2 == 0) sumando = sumando * (-1); solucion = solucion + sumando; contador = contador + 1; } solucion = solucion - sumando; System.out.println("Serie = " + solucion); } } Lógicamente, la solución final correspondiente al programa SerieWhile1 coincide con la del programa SerieWhile.

4.3.1.2 Centinela en la condición de salida de la sentencia while Suele ser una forma muy usual de controlar la finalización de los bucles. Cuando se desconoce el número de datos que se procesarán en un programa y a cada dato le corresponde una iteración, es fácil buscar un dato que cumpla el cometido de avisar para la finalización del proceso repetitivo. Por ejemplo, si se desea conocer la cantidad de números múltiplos de 3 en una serie, podemos plantear un bucle que finalice cuando el dato a procesar sea, por ejemplo 7 al cual llamaremos centinela. En el programa SerieWhileCentinela se van obteniendo números enteros y se distinguen cuáles de ellos son divisibles por 3, además de calcular la cantidad total de los números divisibles, se presentan en pantalla como parte del procesamiento. Para finalizar el proceso se ha elegido como condición que el número obtenido aleatoriamente sea 7.

Page 17: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-17

public class SerieWhileCentinela { public static void main(String[] args) { final int centinela = 7; int numero; int contador; contador = 0; numero = (int) (Math.random()*20); while (numero != centinela) { numero = (int) (Math.random()*20); if (numero % 3 == 0) { contador = contador + 1; System.out.println(numero + "\t(*)"); } else System.out.println(numero); } System.out.println(contador + " múltiplos de 3"); System.out.println("último número = " + numero); } } Obsérvese que el valor de numero se debe fijar aleatoriamente dentro del bucle, pero como en la sentencia while el test se realiza antes de la entrada a cada iteración, es necesario que la variable numero esté inicializada. Para asegurar la primera iteración (la entrada en el cuerpo de la sentencia while) se ha optado por aprovechar la misma asignación que da valor a numero, pero ese valor obtenido fuera del bucle no se emplea para incrementar el contador de múltiplos. Al acabar el programa se comprueba algo que ya es conocido, el último valor de numero es 7, precisamente la condición de salida del bucle.

4.3.2 La sentencia do-while Se ha visto en el programa SerieWhileCentinela que al trabajar con la sentencia while hay ocasiones en que es necesario forzar la entrada a dicho bucle. Una alternativa es utilizar una sentencia repetitiva que compruebe la condición de salida al final del bucle, es el caso de la sentencia do-while, que responde a este esquema:

sentencia_A;

sentencia_C;

sentencia_B;

SI

NO

condicion

sentencias;

sentencia_A; do { sentencias; } while (condicion); sentencia_C;

Page 18: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-18

Como primer ejercicio se plantea modificar el programa SerieWhileCentinela para que funcione con la sentencia do-while, llamaremos SerieDoWhileCentinela al nuevo programa: public class SerieDoWhileCentinela { public static void main(String[] args) { final int centinela = 7; int numero; int contador; contador = 0; do { numero = (int) (Math.random()*20); if (numero % 3 == 0) { contador = contador + 1; System.out.println(numero + "\t(*)"); } else System.out.println(numero); } while (numero != centinela); System.out.println(contador + " múltiplos de 3"); System.out.println("último número = " + numero); } }

4.3.2.1 El control de datos mediante la sentencia do-while Al estudiar la sentencia if se analizó el programa ElCociente donde la condición if de dicho programa evitaba el cálculo del cociente cuando el divisor valía cero. También se dijo que el estudio sistemático de excepciones se relegaría para más adelante, en este momento podemos aplicar la sentencia do-while en lugar de la condición if del programa ElCociente con el mismo objetivo. Al ejecutar el programa ElCocienteDoWhile podemos obtener una salida como esta donde se aprecia que el dividendo puede tomar cualquier valor incluido el cero, pero el divisor necesariamente será distinto de cero:

La sentencia do-while asegura que los valores de la variable divisor sean distintos de cero

Page 19: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-19

El código del programa ElCocienteDoWhile es: public class ElCocienteDoWhile { public static void main(String[] args) { int cociente, dividendo, divisor; for (int k=0; k<=9; k++) { dividendo = (int) (Math.random()*10); do divisor = (int) (Math.random()*10); while (divisor == 0); System.out.print("k=" + k + "\t\t"); cociente = dividendo / divisor; System.out.print(dividendo + "/" + divisor); System.out.println(" = " + cociente); } } } El programa ElCocienteDoWhile tiene cierto interés pues en él se ven dos procesos repetitivos anidados, es decir uno dentro del otro. El bucle externo está controlado por la sentencia for de la que ahora sólo nos interesa saber que, en este caso, hace que se repita diez veces cierta tarea. El bucle interno es el constituido por la sentencia do-while y en cada iteración del bucle for se repetirá las veces necesarias hasta que se cumpla la condición de salida (que el divisor sea cualquier entero distinto de 0, es decir un entero entre 1 y 9).

4.3.2.2 Un ejemplo de la sentencia do-while El conocido algoritmo de Newton para el cálculo numérico de la raíz cuadrada de un número se basa en la siguiente fórmula:

2

11

−−+

=i

ii

RaizRaizDato

Raiz

donde,

Dato es el número de partida, del cual se debe calcular su raíz Raizi es la raíz cuadrada propuesta para la iteración i, que ha sido

hallada gracias a los valores de la anterior iteración i-1 Raiz0 puesto que se trata de la primera iteración no es posible

calcularla gracias a los valores de la anterior iteración, es por ello que se puede poner cualquier valor, por ejemplo la mitad de Dato

En el programa únicamente se toman como datos el número del que se necesita calcular su raíz cuadrada y la precisión requerida para el cálculo (por ejemplo milésima, es decir epsilon = 0.001). Con esa información se plantea un bucle donde se calculen los valores de esta serie: Raiz1 Raiz2 Raiz3 Raiz4 ... Raizn-1 Raizn

Page 20: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-20

Para salir del bucle, se utilizará la condición según la cual la diferencia absoluta entre los valores de dos raíces consecutivas sea menor que la constante epsilon, es decir:

| Raizn-1 - Raizn | < Epsilon El algoritmo se ve en la siguiente tabla, donde en la variable dato hemos guardado el valor de 6 (sabemos que su raíz cuadrada será mayor que 2 pero menor que 3), y la precisión del cálculo es de una milésima, es decir en la constante epsilon se ha guardado 0.001. En cada iteración se calcula la diferencia entre la raizActual y raizAnterior, deteniendo el proceso cuando la diferencia sea menor que epsilon: raizAnterior raizActual Diferencia

inicialización 6 / 2 = 3

al salir de la 1ª iteración 2’5 ½(6/3 + 3) = 2’5 0’5 al salir de la 2ª iteración 2’45 ½(6/2’5 + 2’5) = 2’45 0’04999999 al salir de la 3ª iteración 2’44949 ½(6/2’45 + 2’45) = 2’44949 0’00051020

Esta es la codificación del programa NewtonDoWhile como puede verse la solución (raíz cuadrada del número dato) queda guardada en la variable raizActual, pero ¿cuál es el cometido de la variable de nombre auxiliar?. public class NewtonDoWhile { public static void main(String[] args) { final double epsilon = 0.001; double dato; double raizAnterior; double raizActual; double diferencia; double auxiliar; dato = Math.random()*10; System.out.println("Dato = " + dato); System.out.println("Epsilon = " + epsilon + "\n"); raizAnterior = dato / 2.0; do { auxiliar = raizAnterior; raizActual = ((dato / raizAnterior) + raizAnterior) / 2.0; raizAnterior = raizActual; System.out.println("\nAnterior = " + auxiliar); System.out.println("Actual = " + raizActual); diferencia = Math.abs(raizActual - auxiliar); System.out.println("Diferencia = " + diferencia); } while (diferencia > epsilon); System.out.println("\n\nRaiz aprox. de " + dato + " = " + raizActual); System.out.println("Raiz exacta de " + dato + " = " + Math.sqrt(dato)); } }

Método que devuelve la raíz cuadrada

Page 21: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-21

Si en el programa NewtonDoWhile la variable dato tomara el valor de 6.0 siendo la precisión de una milésima, el resultado sería el siguiente .

Para finalizar este bloque de sentencias repetitivas, se propone escribir la versión del programa NewtonDoWhile pero controlado mediante una sentencia while, es decir con test a la entrada del bucle. Llamar al nuevo programa NewtonWhile.

4.3.3 La sentencia for Tras haber visto en repetidas ocasiones la sentencia for, la última vez en el programa ElCocienteDoWhile, ha llegado el momento de analizarla con mayor detalle. Es importante subrayar que la sentencia for se emplea cuando el número de iteraciones a ejecutar es conocido de antemano, siendo un caso típico este que se muestra:

Primera iteración

Segunda iteración

Tercer iteración

6.0 / 2.0

Menor que epsilon. Se cumple la condición de salida

for (int k=0; k<=9; k++) { }

inicialización

condición

incremento sentencias

Page 22: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-22

La sentencia for se compone de cuatro partes y está gobernada por una variable de control, que normalmente intervendrá en esas cuatro partes que se distinguen en la sentencia for:

Inicialización. Es la zona donde se inicializa, incluso se puede declarar, la variable de control.

Condición. Es la zona donde se define la expresión lógica que indica la realización de los bucles, mientras la condición se cumpla las iteraciones siguen ejecutándose.

Incremento o decremento. En ese lugar se escribe la sentencia que incrementará o decrementará el valor actual de la variable de control.

Sentencias. Es el cuerpo de la sentencia for constituido por las acciones que se ejecutarán en cada iteración.

4.3.3.1 Un ejemplo de la sentencia for con caracteres Se trata de presentar en pantalla la siguiente tabla donde aparecen algunos caracteres del alfabeto ordenados y dispuestos en cuatro filas y cinco columnas:

Entiéndase que no vale hacer cuatro llamadas al método println aportando como argumento las letras, los caracteres se deben escribir de uno en uno y formando la tabla arriba mostrada.

System.out.println("A B C D E"); System.out.println("F G H I J"); System.out.println("K L M N O"); System.out.println("P Q R S T");

Antes de abordar el ejercicio, y puesto que no hemos estudiado los caracteres, comentemos brevemente las nociones más elementales relativas a este tipo de dato. Para declarar e inicializar una variable carácter basta con escribir la siguiente línea, por ejemplo: char letra = 'A'; Las variables de tipo carácter admiten los operadores de incremento y decremento, de forma que después de estas sentencias la variable letra contendrá el carácter C: char letra = 'A'; letra ++; // pasa de tener A a tener B letra ++; // pasa de tener B a tener C Error, operación de incremento no permitida: letra = letra + 1;

Page 23: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-23

Por tanto, tras esa explicación estamos en condiciones de comprender el código del programa ForTabla1 que presenta en el monitor la tabla de letras mayúsculas: public class ForTabla1 { public static void main(String[] args) { int linea, columna; char letra = 'A'; for (linea=1; linea <= 4; linea++) { for (columna=1; columna <= 5; columna++) { System.out.print(letra + "\t"); letra++; } System.out.println(); } } } El programa ForTabla1. se compone de dos sentencias for anidadas. Se comienza por procesar una línea de lo que será la tabla mediante la primera iteración del for externo (la línea correspondiente a la primera iteración determinada por linea=1) mientras esa iteración del for externo se mantiene activa entra en funcionamiento el del for interno, se deben completar todas las iteraciones del for interno antes de que el for externo tome de nuevo el control del flujo para acometer la siguiente iteración (la determinada por linea=2). Esquemáticamente: linea=1 columna=1 linea=2 columna=1 columna=2 columna=2 columna=3 columna=3 columna=4 columna=4 columna=5 columna=5 ...

4.3.3.2 Otro ejemplo de la sentencia for con caracteres El enunciado de este segundo ejercicio nos dice que debemos presentar en pantalla la siguiente tabla donde también aparecen algunos caracteres del alfabeto dispuestos de forma ordenada, pero colocados de acuerdo a un triángulo rectángulo; además los caracteres van cambiando de mayúscula a minúscula alternativamente.

Page 24: 04 Estructuras de Control - docentes.uni.edu.ni de... · Las sentencias de control se asocian en dos grupos bien diferenciados, por una parte las sentencias condicionales que nos

J-M Romo Uriarte 4-24

Como antes, previamente a la resolución del ejercicio, nos permitimos hacer algún comentario que suponemos será útil para su total comprensión. Tanto en la tabla ASCII como en la codificación Unicode la distancia numérica entre la posición de una letra mayúscula y su correspondiente letra minúscula es de 32 unidades. De esta forma si a la A mayúscula le corresponde el numeral 65 a la letra a minúscula le corresponderá la posición 65+32=97. Pero no es posible sumar caracteres entre sí, ni sumarle directamente a un carácter la constante 32. Es necesario aplicar varios forzados de tipo:

letra = (char) ((int)letra + 32); Una posible solución para el triángulo rectángulo de letras lo constituye el programa ForTabla2 que muestra a continuación: public class ForTabla2 { public static void main(String[] args) { final int MAX = 6; int linea, columna; char letra = 'A'; boolean esMayuscula = true; for (linea=1; linea <= MAX; linea++) { for (columna=1; columna <= linea; columna++) { System.out.print(letra + " "); letra++; if (esMayuscula) { letra = (char) ((int)letra + 32); esMayuscula = false; } else { letra = (char) ((int)letra - 32); esMayuscula = true; } } System.out.println(); } } } Igual que en el programa ForTabla1. Se comienza por procesar una línea mediante la primera iteración del for externo (la determinada por linea=1) y cuando el for interno finaliza su tarea, el for externo toma de nuevo el control del flujo para acometer la siguiente iteración (la determinada por linea=2).

Posición del carácter contenido en la variable letra

Posición del carácter minúscula contenido en la variable letra

Carácter minúscula de letra