Ir al contenido principal

ESTRUCTURAS DE CONTROL - PARTE 4

Anidamiento de estructuras repetitivas
En un algoritmo pueden existir varias estructuras repetitivas, siendo sus bucles respectivos anidados o independientes. Se dice que los bucles son anidados cuando están dispuestos de tal modo que unos son interiores a otros. Nótese que, en ningún caso, se admite que sean cruzados, pues su ejecución sería ambigua (Ver Figura 3.14 donde las líneas indican el principio y el fin de cada bucle). 
Fig. 3.14.         Posiciones relativas de los bucles
En las estructuras repetitivas anidadas, la estructura interna debe estar incluida totalmente dentro de la externa, no pudiendo existir solapamiento entre ellas. Las variables índices o de control de los bucles toman valores tales que por cada valor de la variable índice del ciclo externo se ejecuta totalmente el bucle interno.
Ejemplo:
Calcular los factoriales de n números leídos por el teclado.
El problema consiste en realizar una primera estructura repetitiva de n iteraciones del algoritmo de cálculo del factorial, que a su vez se efectúa con una segunda estructura repetitiva.
inicio
leer n {lectura de la cantidad de números}
desde i = 1 hasta n hacer leer NUMERO FACTORIAL ¬ 1
desde j = 1 hasta NUMERO hacer
FACTORIAL ¬FACTORIAL *j
fin_desde
escribir “el factorial del número”, NUMERO, “es”, FACTORIAL
fin_desde 
fin
Nótese que cada valor del contador i, el bucle interno cuyo contador es j, se ejecuta totalmente por lo que las variables que sirven de contadores en ambos bucles deben ser distintas. (No es necesario que las estructuras anidadas sean iguales; podríamos haber anidado un mientras o un repetir dentro del desde).
Bucles infinitos
A pesar de lo dicho hasta ahora, podemos encontrarnos en la práctica con bucles que no exigen una finalización y otros que no la incluyen en su diseño. Por ejemplo, un sistema de reservas de líneas aéreas puede repetir de forma indeterminada un bucle que permita al usuario añadir o borrar reservas sin ninguna condición de finalización. El programa y el bucle se ejecutan siempre, o al menos hasta que el computador se apaga. En otras ocasiones, un bucle no se termina porque nunca se cumple la condición de salida. Un bucle de este tipo se denomina bucle infinito o sin fin. Los bucles infinitos no intencionados, causados por errores de programación, pueden provocar bloqueos en el programa (Ver Ejemplo 11).
Ejemplo:
Escribir un algoritmo que permita calcular el interés producido por un capital a las tasas de interés comprendidos en el rango desde 10 a 20 % de 2 en 2 puntos, a partir de un capital dado.
leer capital tasa ¬10
mientras tasa < > 20 hacer
interés¬ tasa *0.01*capital { tasa*capital/100=tasa*0.01*capital}.
escribir “interés producido”, interés tasa ¬tasa+2
fin_mientras
escribir “continuación”
Los sucesivos valores de la tasa serán 10, 12, 14, 16,18,20, de modo que al tomar ‘tasa’ el valor 20 se detendrá el bucle y se escribirá el mensaje “continuación”. Supongamos que ahora nos interesa conocer este dato de 3 en 3 puntos; para ello se cambia la última línea del bucle por tasa¬ tasa+3. Ahora los valores que tomará tasa serán 10, 13, 16, 19 saltando a 22 y nunca será igual a 20, dando lugar a un bucle infinito y nunca escribiría “continuación”. Para evitarlo deberíamos cambiar la condición de finalización por una expresión del tipo:
tasa < 20 o bien tasa £ 19
El Ejemplo 11, sugiere además que, a la hora de expresar las expresiones booleanas de la condición del bucle, se utilice mayor o menor en lugar de igualdad o desigualdad.
Terminación de bucles con datos de entrada

En el caso, necesariamente muy frecuente, de que leamos una lista de valores por medio de un bucle, se debe incluir algún tipo de mecanismo para terminar la lectura. Existen cuatro métodos para hacerlo:
1.- Simplemente preguntar con un mensaje al usuario, si existen más entradas. Así en el problema de sumar una lista de números el algoritmo sería:
inicio
Suma ¬0
escribir “existen más números en la lista s/n” leer Resp {variable Resp, tipo carácter} mientras Resp = “S” o Resp =“s” hacer
escribir “numero”
leer N
Suma ¬Suma +N
escribir “existen más números (s/n)”
leer Resp fin_mientras
fin
Este método a veces es aceptable e incluso útil, pero es tedioso cuando trabajamos con grandes listas de números, ya que no es muy aconsejable tener que contestar a una pregunta cada vez que introducimos un dato.
2.- Conocer desde el principio el número de iteraciones que ya ha sido visto en los ejemplos anteriores y que permite emplear a una estructura desde-hasta.
3.- Utilizar un valor “centinela”, un valor especial usado para indicar el final de una lista de datos. En estos casos es especialmente importante, de cara al usuario, advertir la forma de terminar la entrada de datos, esto es, qué valor o valores indican el fin de la lista.
Por ejemplo, supongamos que se tienen unas calificaciones, comprendidas entre 0 y 100; un valor centinela en esta lista puede ser -999, ya que nunca será una calificación válida y cuando aparezca se deberá terminar el bucle. Si la lista de datos son números positivos, un valor centinela puede ser un número negativo que indique el final de la lista. El siguiente ejemplo realiza la suma de todos los números positivos introducidos desde el teclado:
suma ¬ 0
leer numero
mientras numero >= 0 hacer
suma ¬ suma + número
leer número
fin_mientras
Obsérvese la ventaja, en este caso, de utilizar una estructura “mientras”, puesto que el último número leído de la lista no se añade a la suma, si es negativo, ya que se sale fuera del bucle.
4.- Por agotamiento de datos de entrada, consiste en simplemente comprobar que no existen más datos de entrada. Ello es posible cuando los datos se obtienen a partir de un fichero, donde se puede detectar el agotamiento de los datos gracias al signo de fin de fichero (EOF “end of file”). En este caso la lectura de un EOF supone la finalización del bucle de lectura. En el caso de estar recibiendo datos de otro computador, el cierre de la conexión también supone el agotamiento de los datos de entrada.
PROGRAMACIÓN MODULAR
Una vez estudiadas las estructuras de control, hemos de ver cómo se implementa la descomposición en módulos independientes denominados subprogramas o subalgoritmos. Un subprograma es una colección de instrucciones que forman una unidad de programación, escrita independientemente del programa principal, con el que se asociará a través de un proceso de transferencia, de forma que el control pasa al subprograma en el momento que se requieran sus servicios y volverá al programa principal cuando aquél se haya ejecutado. Esta descomposición nos interesa por dos razones:
1) Esta asociada al diseño descendente en la programación estructurada, ya que un subprograma, a su vez, puede llamar a sus propios subprogramas e incluso a sí mismo, recursivamente.
2) Permite reutilizar un programa dentro de la resolución de otros problemas distintos, puesto que los subprogramas se utilizan por el programa principal para ciertos propósitos específicos, aquellos pueden haber sido escritos con anterioridad para resolver otros problemas diferentes.
El subprograma recibe datos desde el programa y le devuelve resultados. Se dice que el programa principal llama o invoca al subprograma. Este, al ser llamado, ejecuta una tarea y devuelve el control al programa principal. La invocación puede suceder en diferentes lugares del programa. Cada vez que el subprograma es llamado, el control retorna al lugar desde donde fue hecha la llamada. Algunos subprogramas son tan comunes, que están incluidos en el lenguaje de programación, para ser utilizados directamente en los programas, esto incluye, los propios operadores aritméticos, operaciones sobre cadenas de caracteres, manejo del cursor, etc.
Para poder utilizar esta aproximación, hay que respetar una determinada metodología, denominada abstracción procedimental y que consta de dos etapas:
1) Asignar un nombre que identifique e independice cada módulo.
2) Parametrizar adecuadamente la entrada y la salida, a fin de que los datos que manda al programa principal puedan ser adecuadamente interpretados por el subprograma y viceversa.
Existen dos tipos de subprogramas: funciones y rutinas o procedimientos, aunque no todos los lenguajes distinguen entre ellos. Aquí mantendremos esta distinción, con objeto de facilitar la exposición de la programación modular.

Comentarios