======= Estructuras de control repetitivas ======= Hasta ahora, la cantidad de instrucciones de nuestro programa que se ejecutarán está **acotada**: Esto es, siempre especificamos una lista de instrucciones, y cada una se ejecutará **como mucho una vez** (y algunas podrían no ejecutarse, si utilizamos las [[curso-cpp:estructuras-selectivas|estructuras de control selectivas]]). Sin embargo, si queremos realizar una tarea una única vez, generalmente podríamos realizarla manualmente y listo: nos interesa utilizar la computadora para **automatizar tareas repetitivas**, en las que haya que repetir cómputos mecánicamente una y otra vez, hasta llegar a un resultado. Veremos en esta lección cómo se puede lograr esto en C++. ===== La instrucción while ===== La instrucción ''while'' sirve para instruir a la computadora a que lleve a cabo un determinado conjunto de instrucciones, **mientras se cumpla una condición específica**. La sintaxis (forma de escritura) de esta instrucción es idéntica al ''if'' común (sin ''else''), pero utilizando en su lugar la palabra ''while'': while (condicion) { instruccion1; instruccion2; //... instruccionFinal; } Al igual que ocurría con el ''if'', los paréntesis alrededor de la condición son obligatorios, y las instrucciones del ''while'' se encuentran en un bloque encerrado entre **llaves**. También es posible omitir las llaves si el ''while'' contiene **una sola** instrucción, como en el caso del ''if''. Cuando la computadora se topa con un ''while'', se determina si la condición indicada en el ''while'' es cierta: si no lo es, se saltean todas las instrucciones del ''while'', exactamente igual que ocurre con el ''if''. La diferencia es que, si la condición ocurre, entonces el bloque de instrucciones se ejecuta por completo como ocurría con el ''if'', pero luego de eso **se vuelve a revisar la condición desde el comienzo nuevamente**: la computadora continuará repitiendo el bloque de instrucciones entre llaves una y otra vez, hasta que llegue un punto en que la condición no ocurre. En otras palabras, la computadora continuará ejecutando las instrucciones **mientras** que la condición sea verdadera. Veamos un primer ejemplo sencillo de esto, en el siguiente programa: #include using namespace std; int main() { int x; cin >> x; while (x > 0) { cout << "Recibido " << x << ", que es positivo." << endl; cin >> x; } cout << "El numero " << x << " no es positivo!" << endl; return 0; } Si ejecutamos es programa, por cada número que ingresemos, el programa nos mostrará por pantalla un mensaje con dicho número, y continuará haciendo esto **mientras que el número que ingresamos sea positivo**. Esto es porque en cada paso, se examina la condición ''x>0'', para ver si es positivo el valor almacenado en ''x'', y mientras que lo sea se continúa ejecutando el cuerpo del ''while'': es decir, se muestra el mensaje y se lee en ''x'' un nuevo valor ingresado por el usuario. Recomendamos al lector ejecutar este programa para ver el efecto que tiene, y analizar cómo siguiendo las instrucciones mecánicamente de la forma que hemos explicado, se explica perfectamente el comportamiento de la computadora. Imaginemos ahora otro ejemplo: supongamos que deseamos mostrar por pantalla todos los números desde ''1'' hasta ''N'', una por línea, siendo ''N'' un cierto valor que el usuario pueda ingresar. Como la cantidad de cosas que tenemos que escribir no está prefijada, sino que depende de lo que ingrese el usuario, tendremos que utilizar necesariamente alguna estructura repetitiva, para poder así imprimir muchas veces. Veamos cómo puede llevarse a cabo esta tarea utilizando while. Para eso, deberemos encontrar una **condición** que nos permita saber cuándo seguir escribiendo, y cuándo parar. En estos casos puede ser útil preguntarse lo siguiente: ¿Cómo realizaríamos esta tarea, si tuviéramos que realizarla manualmente, de manera mecánica? Si tuviéramos que escribir por ejemplo, los números del 1 al 1000, lo que haríamos sería **ir contando**: escribimos el 1, **luego pasamos al siguiente número**, que es el 2, y lo escribimos, **luego pasamos al siguiente**, que es el 3, y lo escribimos, y así seguiríamos **mientras no hayamos escrito todos los números que queríamos**. ¿Cómo podemos saber si ya escribimos todos los números? Como solamente queremos escribir números **hasta N**, queremos seguir escribiendo mientras que el número a escribir a continuación **sea menor o igual que N**. Teniendo esto en cuenta, podemos escribir el siguiente programa para mostrar todos los números desde 1 hasta un cierto entero positivo que ingrese el usuario: #include using namespace std; int main() { int N; cin >> N; int x = 1; while (x <= N) { cout << x << endl; x++; // Es lo mismo que x = x + 1 en C++: incrementa el valor de x en 1 } return 0; } Notar que necesitamos utilizar una variable ''x'', que sirve para saber cuál es el numero actual, que vamos a escribir en cada paso. Con esta variable vamos **contando** los números que pasan, comenzando desde el 1, y aumentando en cada paso mientras que sea ''x <''''= N''. Se suele decir que ''x'' es un **contador**. Este mecanismo es muy común, y es el que utilizamos cuando tenemos que repetir ciertas operaciones sobre todo un **rango** de números: utilizamos una variable como contador, de forma tal manera que en el cuerpo del while, realizamos las operaciones utilizando el valor del contador (en nuestro ejemplo, ''x''). Y luego de cada paso, **cambiamos el contador al siguiente valor**, permitiendo que en la siguiente iteración se procese el siguiente número. ===== La instrucción for ===== El patrón del ejemplo anterior es muy común: tenemos una **inicialización** justo antes del ''while'', donde guardamos en el contador un valor inicial adecuado: ''int x = 1;'' en el ejemplo. Luego, tenemos el ''while'' con una **condición** que indica cuándo hay que seguir procesando: ''x <''''= N'' en el ejemplo. Finalmente, para pasar al siguiente número, al final de cada paso aumentamos ''x'' al siguiente valor: usamos en el ejemplo la instrucción ''x++''. Notar que **este patrón es independiente de las operaciones que vayamos a realizar sobre los valores de x**: en cualquier caso en el que queramos **recorrer** todos los números de 1 hasta N para hacer **algo** con ellos, tendremos un código muy similar, con esas 3 partes. Este patrón en 3 partes (inicialización, while con condición, e incremento del contador al final del while) es tan común que existe una instrucción que lo resume para facilitar la lectura de los programas: es la instrucción ''for''. La sintaxis del ''for'' es: for(inicializacion; condicion; incremento) { // Cuerpo de instrucciones a realizar } Es decir, las 3 partes del patrón anterior se ponen todas juntas, entre paréntesis y separadas por punto y coma, en el momento de declarar el for. Un for como el anterior es equivalente a: { inicializacion; while(condicion) { // Cuerpo de instrucciones a realizar incremento; } } De esta forma, el ejemplo anterior generalmente se escribiría utilizando un for, de la siguiente manera: #include using namespace std; int main() { int N; cin >> N; for(int x = 1; x <= N; x++) cout << x << endl; return 0; } Notar que podemos omitir las llaves en este último ejemplo porque el cuerpo del for contiene una única instrucción: el incremento ''x++'' ya no se pone en el cuerpo al utilizar el for. Generalmente, la escritura con for es más clara porque separara la parte de la **iteración**, utilizada para **recorrer los valores de x que nos interesan**, del cuerpo principal donde realizamos las **operaciones** que nos interesan sobre cada valor de x. Además notemos que es válido declarar la variable ''x'' en la parte de inicialización del for: El ámbito de dicha variable es todo el contenido del for (tanto el encabezado de 3 partes, como el cuerpo de instrucciones). La variable ''x'' declarada en la inicialización del for **no puede** utilizarse fuera del for. Si se vuelve a realizar otro for similar, se estaría utilizando una variable x **diferente**. ==== Ejercicios ==== - Escribir un programa que lea un número ''N'', y escriba en pantalla un solo número: la suma de todos los números desde 1 hasta ''N''. - Escribir un programa que lea un número ''N'', y escriba en pantalla un solo número: la suma de todos los números desde 1 hasta ''N'' que sean múltiplos de 3. - Escribir un programa que lea un número ''N'', y escriba en pantalla un solo número: la suma de todos los números desde 1 hasta ''N'' que sean múltiplos de 3 pero no de 5. - Escribir un programa que lea un número ''N'', y escriba en pantalla un solo número: el producto de todos los números desde 1 hasta ''N'' (a este número se lo conoce como **N factorial**, y se escribe N!). - Escribir un programa que lea un número ''N'', y escriba en pantalla un solo número: la suma todos los números desde 1 hasta ''N'', pero elevados al cuadrado (por ejemplo, para N=3 la respuesta es 14=1+4+9). ==== Soluciones a los ejercicios ==== - El siguiente código muestra un ejemplo de solución: #include using namespace std; int main() { int N, suma = 0; cin >> N; for(int x = 1; x <= N; x++) suma = suma + x; cout << suma << endl; return 0; } Se puede observar que utilizamos una variable auxiliar ''suma'', que comienza en ''0'', y en cada paso lo que hacemos sumarle el número actual. De esta forma, como en cada paso el valor de ''suma'' es aumentado en el número correspondiente, al final del proceso tendrá la suma de todos los números, y por eso escribimos su valor al final. La instrucción ''suma = suma + x'' justamente aumenta el valor de suma, porque lo que allí se indica es que se guarde en la variable suma, el valor que hay ahora en la variable suma, más el valor de x. Esta instrucción también puede abreviarse en C++ como ''suma += x'' (similarmente, existen operadores ''-='' para restar, ''*='' para multiplicar, etc). - Este ejemplo es muy parecido al anterior: basta agregarle un if para que solamente se ejecute la operación de suma, cuando el número actual es múltiplo de 3. #include using namespace std; int main() { int N, suma = 0; cin >> N; for(int x = 1; x <= N; x++) if (x % 3 == 0) suma += x; cout << suma << endl; return 0; } - #include using namespace std; int main() { int N, suma = 0; cin >> N; for(int x = 1; x <= N; x++) if (x % 3 == 0 && x % 5 != 0) suma += x; cout << suma << endl; return 0; } - Este ejercicio es casi igual al primero, pero en lugar de sumar los números, queremos multiplicarlos. Utilizaremos ''*'' entonces en lugar de ''+'' para la operación. #include using namespace std; int main() { int N, producto = 1; cin >> N; for(int x = 1; x <= N; x++) producto *= x; cout << producto << endl; return 0; } Un detalle importante es que al calcular un producto, **inicializamos la variable auxiliar en uno**, y no en cero como hacíamos en el caso de la suma. Notar que como le vamos multiplicando cada vez más números, si comenzara en cero, quedaría en cero para siempre. Los factoriales son números que crecen muy rápidamente: si ejecutamos el programa, veremos que ya con valores de ''N'' mayores que 12 obtenemos resultados demasiado grandes para el tipo de datos int, con lo cual veremos resultados erróneos, y a veces incluso negativos. - #include using namespace std; int main() { int N, suma = 0; cin >> N; for(int x = 1; x <= N; x++) suma += x*x; cout << suma << endl; return 0; } ===== La instrucción do-while ===== La última estructura de control repetitiva es la instrucción do-while, que es la menos utilizada de todas. Esta instrucción funciona igual que ''while'', pero con la única salvedad de que la condición se verifica **luego** de cada paso, en lugar de **antes** de cada paso. Por lo tanto, do-while siempre realiza al menos un paso, mientras que con un while, si la condición es falsa al comenzar, no se realiza ningún paso. do { // cuerpo de instrucciones a repetir } while (condicion); ===== Ejercicios ===== * [[http://juez.oia.unsam.edu.ar/#/task/pares_impares/statement|Escribir un programa que lea un numero N, y luego imprima la suma de los números pares, menos la suma de los impares, para los primeros N naturales]]. Por ejemplo, para N=3 sería 2 - (1+3), para N=6 sería (2+4+6)-(1+3+5), para N=1 sería -1. * [[http://juez.oia.unsam.edu.ar/#/task/pollo_pan/statement|Escribir un programa que lea una palabra, y la imprima encerrada en un cuadrado de asteriscos]]. Ejemplos: Si lee "pollo" imprime: ******* *pollo* ******* Si lee "pan" imprime: ***** *pan* ***** * [[http://juez.oia.unsam.edu.ar/#/task/max_min_sum/statement|Escribir un programa que lea N números, y visualice el máximo, el mínimo y la suma]]. El valor de N se solicita al comenzar. * [[http://juez.oia.unsam.edu.ar/#/task/string_reverser/statement|Escribir un programa que lea una palabra, y la imprima al revés (leída de derecha a izquierda)]]. * Un número es perfecto, cuando la suma de sus divisores es igual al mismo número. [[http://juez.oia.unsam.edu.ar/#/task/busca_perfectos/statement|Crear un programa que dado un N, busque y encuentre todos los números perfectos hasta N]]. * Un número es primo, cuando no tiene divisores que no sean 1 o el mismo número. [[http://juez.oia.unsam.edu.ar/#/task/busca_primos/statement|Hacer un programa que imprima los primos hasta N, para N dado por la entrada]].