- Sentencias compuestas (bloques de código)
- Declaraciones y definiciones
- Sentencias simples (expresiones)
- Visión general de las sentencias de control
- Operador cíclico for
- Operador cíclico while
- Operador cíclico do
- Operador condicional if
- Operador switch
- Operador break
- Operador continue
- Operador return
- Sentencia vacía
Operador cíclico For
Este bucle se implementa mediante una sentencia con la palabra clave for, de ahí su nombre. De forma generalizada, puede describirse como sigue:
for ( [initialization] ; [condition] ; [expression] )
|
En el título, tras la palabra 'for', se indica lo siguiente entre paréntesis:
- Inicialización: sentencia para una inicialización de una sola vez antes del inicio del bucle;
- Condición: condición booleana que se comprueba al principio de cada iteración, y el bucle se ejecuta mientras sea verdadera;
- Expresión: fórmula de los cálculos realizados al final de cada iteración, cuando se han pasado todas las sentencias del cuerpo del bucle.
El cuerpo del bucle es una sentencia simple o compuesta.
Los tres componentes del encabezado son opcionales y pueden omitirse en cualquier combinación, incluida su ausencia.
La inicialización puede incluir la declaración de variables (junto con el establecimiento de valores iniciales) o la asignación de valores a variables ya existentes. Dichas variables se denominan variables de bucle. Si se declaran en el encabezado, su alcance y duración se limitan al bucle.
El bucle comienza a ejecutarse si, tras la inicialización, la condición es true, y continúa ejecutándose mientras sea cierta al comienzo de cada iteración posterior. Si durante la siguiente comprobación se incumple la condición, el bucle sale, es decir, el control se transfiere a la sentencia escrita después del bucle y su cuerpo. Si la condición es false antes del inicio del bucle (después de la inicialización), nunca se ejecutará.
La condición y la expresión suelen incluir variables de bucle.
Ejecutar un bucle significa ejecutar su cuerpo.
La forma más común del bucle for tiene una única variable de bucle que controla el número de iteraciones. En el siguiente ejemplo calculamos los cuadrados de los números del array a.
int a[] = {1, 2, 3, 4, 5, 6, 7};
|
Este bucle se ejecuta en los siguientes pasos:
- Se crea una variable i con un valor inicial 0.
- Se comprueba si la variable i es menor que el tamaño del bucle n. Mientras ello sea cierto, el bucle continúa. Si es falso, saltamos a la sentencia que llama a la función ArrayPrint .
- Si la condición es verdadera, se ejecutan las sentencias del cuerpo del bucle. En este caso, el i-ésimo elemento del array obtiene el producto del valor inicial de este elemento por sí mismo, es decir, el valor de cada elemento se sustituye por su cuadrado.
- La variable i se incrementa en 1.
A continuación se repite todo, empezando por el paso 2. Después de salir del bucle, su variable i se destruye, y cualquier intento de acceder a ella causará un error.
La expresión para el paso 4 puede ser de complejidad arbitraria, no sólo un incremento de la variable del bucle. Por ejemplo, para iterar sobre elementos pares o impares, se podría escribir i += 2.
Independientemente del número de sentencias que compongan el cuerpo del bucle, se recomienda escribirlo en una línea (o líneas) separada del encabezado. Esto facilita el proceso de depuración paso a paso.
La inicialización puede incluir múltiples declaraciones de variables, pero deben ser del mismo tipo porque son una sola declaración. Por ejemplo, para reordenar los elementos en orden inverso, puede escribir un bucle de este tipo (esto es sólo una demostración del bucle; existe una función ArrayReverse integrada que permite invertir el orden en un array, véase Copia y edición de arrays):
for(int i = 0, j = n - 1; i < n / 2; ++i, --j)
|
La variable auxiliar temp se crea y elimina en cada pasada del bucle, pero el compilador le asigna memoria una sola vez, como a todas las variables locales, al entrar en la función. Esta optimización funciona bien para los tipos integrados. Sin embargo, si un objeto de clase personalizado se describe en el bucle, entonces su constructor y su destructor se llamarán en cada iteración.
Es aceptable cambiar la variable de bucle en el cuerpo del bucle, pero esta técnica sólo se utiliza en casos muy excepcionales. No se recomienda hacerlo, ya que puede dar lugar a errores (en particular, pueden saltarse elementos procesados o la ejecución puede entrar en un bucle infinito).
Para demostrar la capacidad de omitir componentes de encabezado, imaginemos el siguiente problema: necesitamos encontrar el número de elementos del mismo array cuya suma es menor que 100. Para ello, necesitamos una variable contador k definida antes del bucle porque debe seguir existiendo después de su finalización. También crearemos la variable sum para calcular la suma de forma acumulativa.
int k = 0, sum = 0;
|
Así, no hay necesidad de hacer la inicialización en el encabezado. Además, el contador k se incrementa utilizando un post-incremento directamente en la expresión que calcula la suma (al acceder a un elemento del array). Por lo tanto, no necesitamos una expresión en el título.
Al final del bucle imprimimos k y la suma menos el último elemento añadido, porque fue el que superó nuestro límite de 100.
Observe que estamos utilizando un bloque compuesto aunque sólo haya una sentencia en el cuerpo del bucle. Esto es útil porque, cuando el programa crece, ya está todo listo para añadir sentencias adicionales dentro de los corchetes. Además, este enfoque garantiza un estilo uniforme para todos los bucles. La elección, en cualquier caso, corresponde no obstante al programador.
En la versión explícita y más abreviada posible, el encabezado del ciclo podría tener este aspecto:
for( ; ; )
|
Si no hay sentencias en el cuerpo de un bucle de este tipo que interrumpan el bucle debido a algunas condiciones, este se ejecutará indefinidamente. Aprenderemos a romper y probar condiciones en Break jump y If selection respectivamente.
Estos algoritmos de bucle suelen utilizarse en los servicios (están diseñados para un trabajo constante en segundo plano) para supervisar el estado del terminal o de los recursos de red externos. Suelen contener sentencias que pausan el programa en un intervalo especificado, por ejemplo mediante la función integrada Sleep. Sin esta precaución, un bucle infinito cargará el 100 % del núcleo de un procesador.
El script StmtLoopsFor.mq5 contiene un bucle infinito al final, pero es sólo para fines de demostración.
for( ; ; )
|
En el bucle, una vez por segundo, el temporizador interno del ordenador (GetTickCount) se muestra mediante la función Comment: el valor aparece en la esquina superior izquierda del gráfico. Sólo el usuario puede interrumpir el bucle borrando todo el script del gráfico (botón «Eliminar» del cuadro de diálogo Expertos). Este código no comprueba si el usuario solicita detenerse dentro del bucle, aunque existe una función integrada IsStopped para este fin. Devuelve true si el usuario ha dado la orden de parar. En el programa, especialmente si hay bucles y cálculos a largo plazo, es conveniente prever la comprobación del valor de esta función y terminar voluntariamente el bucle y todo el programa al recibir true. De lo contrario, el terminal terminará forzosamente el script al cabo de 3 segundos de espera (con salida al registro «Terminación anormal»), lo que ocurrirá en este ejemplo.
Una versión mejor de este bucle debería ser:
for( ; !IsStopped(); ) // continue until user interrupt
|
Sin embargo, este bucle se implementaría mejor utilizando otra sentencia de repetición while. Por regla general, un bucle for sólo debe utilizarse cuando hay una variable de bucle obvia y/o un número predeterminado de iteraciones. En este caso no se cumplen estas condiciones.
Las variables de bucle suelen ser números enteros, aunque se permiten otros tipos, como double. Esto se debe a que la propia lógica del funcionamiento del bucle implica la numeración de las iteraciones. Además, siempre es posible calcular los números reales necesarios a partir de un índice entero, y con mayor precisión. Por ejemplo, el siguiente bucle itera sobre valores de 0.0 a 1.0 en incrementos de 0.01:
for(double x = 0.0; x < 1.0; x += 0.01) { ... } |
Se puede sustituir por un bucle similar con una variable entera:
for(int i = 0; i < 100; ++i) { double x = i * 0.01; ... } |
En el primer caso, al sumar x += 0.01, el error de los cálculos de punto flotante se acumula gradualmente. En el segundo caso, cada valor x se obtiene en una operación i * 0.01, con la máxima precisión disponible.
Es habitual dar a las variables de bucle los siguientes nombres de una sola letra, como por ejemplo, i, j, k, m, p, q. Se necesitan varios nombres cuando se anidan bucles o se calculan índices hacia delante (crecientes) y hacia atrás (decrecientes) dentro del mismo bucle.
Por cierto, aquí tienes un ejemplo de bucle anidado. El siguiente código calcula y almacena la tabla de multiplicar en un array bidimensional.
int table[10][10] = {0};
|