Fundamentos de Programación Tema 5: Estructuras de Control Dpto. de Ingeniería de Sistemas Telemáticos http://www.lab.dit.upm.es/~fprg/fprg.html oct-07 1 Indice Orden de Ejecución, Programación Estructurada Alternativas if switch Estructuras repetitivas while do ... while for Ruptura de secuencia Saltos: break y continue Excepciones (throw) Recursividad oct-07 2 Programación estructurada Una forma de estructurar los programas para que no sean un laberinto, sino algo fácil de entender por uno mismo y por los demás La idea fundamental es que los programas son un conjunto de bloques que encajan entre sí sobre la premisa de que cada bloque tiene un sólo punto de entrada y un sólo punto de salida, y la salida de uno se enchufa a la entrada del siguiente o sea, un lego oct-07 3 1 Secuencias y bloques { sentencia 1; // un bloque sentencia sentencia sentencia sentencia ... ... ... sentencia sentencia 2; sentencia 3; sentencia 4; 1; 2; 3; 4; n; } el “;” es terminador sentencia n; oct-07 4 Ejecución condicional true condición false sentencia; private int monedas (int aDevolver, int valor) { int cuantas= aDevolver / valor; if (cuantas > 0) System.out.println (cuantas + " de " + valor); return aDevolver % valor; } oct-07 5 ¿Hay que poner llaves? Caso de duda, póngase if (condición) sentencia1; sentencia2; sentencia3; se entiende como if (condición) sentencia1; sentencia2; sentencia3; que no es lo mismo que if (condición) { sentencia1; sentencia2; sentencia3; } oct-07 6 2 Ejecución condicional public static void main (String args[]) { if (args.length != 2) { System.out.println ("uso: java MaquinaCambio precio entrego"); System.exit(1); } MaquinaCambio panaderia= new MaquinaCambio(); panaderia.daCambio(Integer.parseInt(args[0]), Integer.parseInt(args[1])); } oct-07 7 Ejecución condicional true condición false sentencia 2; if (hora < 14) System.out.println (“buenos dias”); else System.out.println (“buenas tardes”); sentencia 1; oct-07 8 Ambigüedad if (a) if (b) S1; else S2; significa if (a) { if (b) S1; else S2; } oct-07 9 3 Selección múltiple true expr=val1 sentencia1; false true expr=val2 sentencia2; false ... false true expr=valN if (mes == 1) ...print(“enero”); else if (mes == 2) ...print(“febrero”); else if (mes == 3) ...print(“marzo”); ... ... else ...print(“no se”); sentenciaN; false sentenciaD; ¡¡La expresión se evalúa N veces!! Efectos laterales oct-07 10 Selección múltiple if (mes == 1) ...print(“enero”); else if (mes == 2) ...print(“febrero”); else if (mes == 3) ...print(“marzo”); ... ... else ...print(“no se”); switch (mes) { case 1: ...print(“enero”); break; case 2: ...print(“febrero”); break; ... default: ...print(“no se”); oct-07 11 Selección múltiple switch (mes) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: System.out.println(“31 dias”); break; case 4: case 6: case 9: case 11: System.out.println(“30 dias”); break; case 2: if (bisiesto) System.out.println(“29 dias”); else System.out.println(“28 dias”); break; default: System.out.println(“no se”); } oct-07 12 4 Sentencias switch Sólo funcionan sobre enteros, booleanos o caracteres ¿por qué? Chequean que no hay duplicados las condiciones tienen que ser excluyentes Mejor legibilidad Sin “break”, el programa no saltaría al final El “default” es opcional oct-07 13 Bucles Se repite una acción N veces modelo while se repite 0 o más veces modelo hasta se repite 1 o más veces modelo for se repite un número determinado de veces oct-07 14 while condición true sentencia; while (!aprobado) me.examino(); false int n= ...; int fact= 1; while (n > 0) { fact*= n; n--; } oct-07 while (hace.sol()) me.banno(); 15 5 do { ... } while int i; double e, ei; i= 0; ei= 1; e= ei; sentencia; condición true do { i++; ei= ei/i; e= e+ei; System.out.println ("e= " + e); } while (ei > 1e-10); false oct-07 16 for for (int i= 0; i < 10; i++) ...print(i); inicialización; actualización; condición false true acción; for (int i= 10; i > 0; i--) ...print(i); int n= ...; int fact= 1; for (n= 5; n > 0; n--) fact*= n; oct-07 17 Ejemplo Imprimir argumentos de línea de llamada class ImprimeArgumentos { public static void main (String args[]) { for (int i=0; i < args.length; i++) { System.out.print (“argumento “ + i); System.out.println(“: “ + args[i]); } } } oct-07 18 6 Variables de control Se puede inicializar cualquier número de variables del mismo tipo for (int i= 0, j= 10; i < 9 && j > 0; i++, j--) acción ; Si estaba declarada antes, al acabar el for queda con el valor final Si se declara en el for, sólo existe en el bucle, por lo tanto su valor no podrá ser usado fuera La inicialización, condición y actualización son opcionales (¡ojo!) oct-07 19 Elección de estructura 1. Si nº fijo de veces, elegir for 2. Si 1 ó más veces, elegir do { } while (); 3. Si 0 ó más veces, elegir while () {} se trata de elegir la que mejor se ajuste al concepto para mejorar la legibilidad oct-07 20 Ruptura La secuencia de control de un programa se puede romper de tres maneras: saltos (break) bucle parcial (continue) excepciones (Exception) Saltos: permiten alterar el flujo de control en los bucles y en switch Bucle parcial: no se ejecuta todo el bucle Excepciones: alteran el flujo de control cuando se produce un error en alguna parte del programa oct-07 21 7 break Vámonos directamente a la salida Utilizable en {}, while, do/while, for y switch int n= 0; while (true) { ...print(n); if (n == 5) break; n++; } for (int n= 0; ; n++) if (n == 5) break; oct-07 22 continue La secuencia continue, dentro de un bucle, comienza inmediatamente la siguiente iteración del ciclo for (int n= 0; n < 100; n++) { if (n % 2 == 0) continue; ...print(n); } oct-07 23 Saltos etiquetados break causa la salida del bucle más interno (cercano), el que lo encierra directamente. Para salirnos de una serie de estructuras anidadas de una sola vez, podemos usar la sentencia break etiquetada break bucleExterno; continue sigue con la iteración del bucle que lo encierra directamente continue etiquetado salta el resto del cuerpo de esta estructura y todas aquellas hasta seguir con la estructura etiquetada continue etiqueta; oct-07 24 8 Errores frecuentes Hacer una vez de más o de menos Equivocarse en caso inicial o final Probar casos extremos: condición que no se satisface, sólo se satisface una vez,... Comprobar que las variables están correctamente inicializadas No poner condiciones exactas: por exceso mejor while (i != 10) ... while (i < 10) ... // por si acaso No poner condiciones == ni != sobre reales oct-07 25 Excepciones (I) Las excepciones ofrecen salidas de emergencia de bucles de llamadas a métodos Se pretende que el programador se centre en el comportamiento “normal” (si todo va bien) y que la situación excepcional aborte oct-07 26 Excepciones (II) Se inventan unos objetos especiales Se clasifican como throwable (arrojables) Se pueden crear new Exception(); new Exception(“me ha pasado esto”); Se pueden lanzar throw new Exception(“me rindo”); Se pueden atrapar al vuelo catch (Exception e) { System.err.println(e); } oct-07 27 9 Excepciones (III) Su activación interrumpe la ejecución secuencial Se propagan hasta que son capturadas interrumpe toda sentencia que no la capture La ejecución continua si existe cláusula catch que acepte la excepción activada Pueden ser lanzadas por un programa con la sentencia throw el intérprete puede activarlas por su cuenta oct-07 28 Ejemplo public static void main (String[] arg) throws Exception { for (int a= 0; a < arg.length; a++) { int n= Integer.parseInt(arg[a]); System.out.println(100 / n); } } Va dividiendo 100 por una serie de dividendos; si un dividendo fuera 0: explota. oct-07 29 Ejemplo public static void main (String[] arg) { for (int a= 0; a < arg.length; a++) { try { int n= Integer.parseInt(arg[a]); System.out.println(100 / n); } catch (Exception e) { System.err.println (e); } } } Detecta la división por 0, avisa y sigue oct-07 30 10 Ejemplo public static void main (String[] arg) { for (int a= 0; a < arg.length; a++) { try { int n= Integer.parseInt(arg[a]); if (n == 0) throw new Exception (“no se dividir por 0”); System.out.println(100 / n); } catch (Exception e) { System.err.println (e); } } } Detecta la división por 0, avisa y sigue oct-07 31 try {...} catch (args) {...} Si no se lanza nada, el catch como si no existiera Dentro del try se lanza (throw) una excepción Se olvida el resto del código Se ejecuta lo que diga el catch try { ... throw new Exception(); ... } catch (Excepcion e) { } oct-07 32 try ... catch ... finally Se puede añadir un trozo finally que se ejecuta bien cuando acaba el código normal (try) bien cuando acaba el código excepcional (catch) ... es decir, SIEMPRE se ejecuta; incluso si try lanza una excepción que no captura ningún catch oct-07 33 11 catch try { ... ... ... } catch (claseA ida) { ... ... ... } catch (claseB idb) { ... ... ... } catch (classC idc) { ... ... ... Los diferentes catch se intentan en el orden en que aparecen hasta que uno de ellos casa; después de casar con uno, los demás se olvidan Casar significa que la excepción a agarrar es de la clase indicada o de una clase extensión de ella (aplicándose un upcasting) oct-07 34 finally try { ... ... ... throw new Exception(); ... ... ... } catch (Exception e) { ... ... ... } finally { ... ... ... } try { ... ... ... throw new Exception(); ... ... ... } catch (Exception e) { ... ... ... } finally { ... ... ... } oct-07 35 Excepciones anidadas Sólo puede haber una excepción “en el aire” en un momento dado Si un catch atrapa una excepción, ésta deja de propagarse El catch puede a su vez lanzar una excepción La vieja catch (Excepcion e) { ...; throw e; } Una nueva catch (Excepcion e) { ...; throw new Excepcion(“...”); } oct-07 36 12 Métodos y excepciones Un método puede, en su cuerpo, lanzar una excepción con el ánimo de que sea recogida en el exterior (por aquél que lo invocó) Debe indicar en la cabecera que puede lanzar una excepción nótese que puede lanzarla o no es una opción, no una obligación metodo (argumentos) throws Exception { ... throws new Exception(); ... } oct-07 37 ¿Cuándo usar excepciones? Aunque es un mecanismo de control de flujo de ejecución conviene NO usarlo para programar lo que es normal Conviene usarlo cuando hay que programar una situación anómala el que lo lea se pondrá en situación mental de analizar una situación excepcional oct-07 38 Jerarquía de excepciones Throwable Error Error Error Exception RuntimeException RuntimeException RuntimeException del delprogramador programador del programador oct-07 39 13 Throwable public class Throwable { public Throwable (); public Throwable (String mensaje); public String toString (); public void printStackTrace (); public void printStackTrace (java.io.PrintStream s); public String getMessage (); } oct-07 40 Herederas public class Error extends Throwable { public Error (); public Error (String mensaje); } public class Exception extends Throwable { public Exception (); public Exception (String mensaje); } oct-07 41 Obligación de declarar Las excepciones de clase Exception y derivadas deben declararse en la cabecera de los métodos que las puedan lanzar o capturadas (catch) Las excepciones de clase Error y RuntimeError van implícitas oct-07 42 14 Recursividad oct-07 43 Recursividad Forma de cálculo equivalente a las demostraciones por inducción El resultado para N se calcula en función del resultado para N-1 y así, recursivamente, hasta llegar a un caso 0 n = 0, n = 1 1 f ( n) = n * f ( n − 1) n > 1 int fact (int n) throws Exception { if (n < 0) throw new Exception (“argumento no válido”); if ( (n == 0) || (n == 1) ) return 1; else return n * fact (n-1); } oct-07 44 Recursividad Algunos problemas admiten una solución recursiva muy sencilla de comprender y de demostrar que es correcta matemáticamente más legibles caso básico + recursividad oct-07 45 15 Ámbito dinámico (recursivo) i=5 x=f(5) if (i<=1) 5*f(4) i=4 if (i<=1) 4*f(3) i=1 ... return 5*24 x=120 if (i<=1) return 1 return 4*6 oct-07 46 Ámbito dinámico (recursivo) i=5 x=f(5) if (i<=1) 5*f(4) i=4 if (i<=1) 4*f(3) i=1 ... return 5*24 x=120 if (i<=1) return 1 return 4*6 oct-07 47 Contextos anidados fact (5) int n= 5 return n*fact(4) fact (4) int n= 4 return n*fact(3) fact (3) int n= 3 return n*fact(2) fact (2) int n= 2 return n*fact(1) fact (1) int n= 1 return 1 oct-07 48 16 Limitaciones Si se crean muchísimos contextos, llega un momento que no caben en memoria y se produce desbordamiento stack overflow Ocurre cuando el problema es demasiado grande para la memoria del sistema número de contextos * número de variables cuando no converge al caso 0 error de diseño de la solución oct-07 49 Fibonacci (I) f ( 0) = 1 f (1) = 1 fib(4) f (n) = f (n − 2) + f (n − 1) int fib (int n) { if (n < 2) return 1; return fib(n-1) + fib(n-2) } fib(3) fib(2) fib(1) fib(2) fib(1) fib(1) fib(0) fib(0) Solución recursiva Matemáticamente impecable Pero muy costosa en recursos o sea, muy mala solución como ingeniería oct-07 50 Fibonacci (II) int fib (int n) { return aux (1, 1, n); } fib(4) aux(1, 1, 4) private int aux (int n1, int n2, int n) { if (n < 1) return n1; return aux(n2, n1+n2, n-1) } aux(1, 2, 3) aux(2, 3, 2) aux(3, 5, 1) Recursión con acumulación: solución económica en memoria heap overflow ¿es más rápida? oct-07 aux(5, 8, 0) 5 51 17 Fibonacci (III) int fib (int n) { int n1= 1; int n2= 1; for (int i= 0; i < n; i++) { int s= n1 + n2; n1= n2; n2= s; } return n1; } solución iterativa solución muy económica en memoria ¿es más rápida? oct-07 52 Recursión e iteración while (condición) acción; intercambiable ... función(..) { if (!condición) return; acción; función(...); } ¿Qué solución es más rápida? Pues depende de si gana la batalla la habilidad del programador o la capacidad de optimización del compilador Hay que medir en el laboratorio Si quedan empatados, se elige la solución más legible suele ganar el compilador: ¿piloto F1 o conductor normal? oct-07 53 Torres de Hanoi origen auxiliar destino Torres de Hanoi: Mover los discos del poste origen al destino No se puede poner un disco mayor sobre otro menor Sólo se mueve un disco a la vez El poste restante se puede usar como auxiliar oct-07 54 18 Torres de Hanoi Solución iterativa compleja Mejor solución recursiva: mover n-1 discos de poste origen a poste auxiliar mover un disco de origen a destino mover n-1 discos de poste auxiliar a destino oct-07 55 Torres de Hanoi n-1 1 n-1 2 n-1 3 4 n-1 oct-07 56 Torres de Hanoi class Hanoi { public static void torres (int ndiscos, int orig, int dest, int aux) { if (ndiscos == 1) { System.out.println (orig + “ ---> “ + dest); return; } torres (ndiscos -1, orig, aux, dest); System.out.println (orig + “ ---> “ + dest); torres (ndiscos -1, aux, dest, orig); } public static void main (String[ ] args) { System.out.println (“Cuantos discos: “); int n = SimpleIO.readInt(); torres (n, 1, 3, 2); } } oct-07 57 19