A menudo necesitamos repetir acciones.
Por ejemplo, generar productos de una lista uno tras otro o simplemente ejecutar el mismo código para cada número del 1 al 10.
Los bucles son una forma de repetir el mismo código varias veces.
Los bucles for…of y for…in
Un pequeño anuncio para lectores avanzados.
Este artículo cubre solo los bucles básicos: while
, do..while
y for(..;..;..)
.
Si llegó a este artículo buscando otros tipos de bucles, estos son algunos consejos:
Consulte for…in para recorrer las propiedades del objeto.
Consulte for…of y iterables para recorrer matrices y objetos iterables.
De lo contrario, sigue leyendo.
El bucle while
tiene la siguiente sintaxis:
mientras (condición) { // código // el llamado "cuerpo del bucle" }
Mientras la condition
sea verdadera, se ejecuta el code
del cuerpo del bucle.
Por ejemplo, el siguiente bucle genera i
mientras i < 3
:
sea yo = 0; while (i < 3) { // muestra 0, luego 1, luego 2 alerta (yo); yo ++; }
Una única ejecución del cuerpo del bucle se denomina iteración . El bucle del ejemplo anterior realiza tres iteraciones.
Si faltara i++
en el ejemplo anterior, el bucle se repetiría (en teoría) para siempre. En la práctica, el navegador proporciona formas de detener dichos bucles y, en JavaScript del lado del servidor, podemos finalizar el proceso.
Cualquier expresión o variable puede ser una condición de bucle, no solo comparaciones: la condición se evalúa y se convierte a booleana mediante while
.
Por ejemplo, una forma más corta de escribir while (i != 0)
es while (i)
:
sea yo = 3; while (i) { // cuando i se vuelve 0, la condición se vuelve falsa y el ciclo se detiene alerta (yo); i--; }
No se requieren llaves para un cuerpo de una sola línea
Si el cuerpo del bucle tiene una sola declaración, podemos omitir las llaves {…}
:
sea yo = 3; mientras que (i) alerta (i--);
La verificación de condición se puede mover debajo del cuerpo del bucle usando la sintaxis do..while
:
hacer { // cuerpo del bucle } mientras (condición);
El bucle primero ejecutará el cuerpo, luego verificará la condición y, mientras sea verdadera, la ejecutará una y otra vez.
Por ejemplo:
sea yo = 0; hacer { alerta (yo); yo ++; } mientras (yo < 3);
Esta forma de sintaxis solo debe usarse cuando desee que el cuerpo del bucle se ejecute al menos una vez, independientemente de que la condición sea verdadera. Por lo general, se prefiere la otra forma: while(…) {…}
.
El bucle for
es más complejo, pero también es el bucle más utilizado.
Se parece a esto:
for (comienzo; condición; paso) { // ... cuerpo del bucle ... }
Aprendamos el significado de estas partes con un ejemplo. El siguiente bucle ejecuta alert(i)
para i
desde 0
hasta (pero sin incluir) 3
:
for (let i = 0; i < 3; i++) { // muestra 0, luego 1, luego 2 alerta(i); }
Examinemos la for
for parte por parte:
parte | ||
---|---|---|
comenzar | let i = 0 | Se ejecuta una vez al ingresar al bucle. |
condición | i < 3 | Comprobado antes de cada iteración del bucle. Si es falso, el ciclo se detiene. |
cuerpo | alert(i) | Se ejecuta una y otra vez mientras la condición sea verdadera. |
paso | i++ | Se ejecuta después del cuerpo en cada iteración. |
El algoritmo de bucle general funciona así:
inicio de ejecución → (si condición → ejecutar cuerpo y ejecutar paso) → (si condición → ejecutar cuerpo y ejecutar paso) → (si condición → ejecutar cuerpo y ejecutar paso) → ...
Es decir, begin
se ejecuta una vez y luego se itera: después de cada prueba condition
, se ejecutan body
y step
.
Si es nuevo en los bucles, podría resultarle útil volver al ejemplo y reproducir cómo se ejecuta paso a paso en una hoja de papel.
Esto es exactamente lo que sucede en nuestro caso:
// para (sea i = 0; i < 3; i++) alerta(i) // inicio de ejecución sea yo = 0 // si condición → ejecutar cuerpo y ejecutar paso si (i < 3) { alerta(i); yo ++ } // si condición → ejecutar cuerpo y ejecutar paso si (i < 3) { alerta(i); yo ++ } // si condición → ejecutar cuerpo y ejecutar paso si (i < 3) { alerta(i); yo ++ } // ...terminar, porque ahora i == 3
Declaración de variables en línea
Aquí, la variable "contadora" i
se declara directamente en el bucle. Esto se denomina declaración de variable "en línea". Estas variables sólo son visibles dentro del bucle.
para (sea i = 0; i < 3; i++) { alerta(i); // 0, 1, 2 } alerta(yo); // error, no existe tal variable
En lugar de definir una variable, podríamos usar una existente:
sea yo = 0; for (i = 0; i < 3; i++) { // usar una variable existente alerta(yo); // 0, 1, 2 } alerta(yo); // 3, visible, porque se declara fuera del bucle
Se puede omitir cualquier parte de for
.
Por ejemplo, podemos omitir begin
si no necesitamos hacer nada al inicio del ciclo.
Como aquí:
sea yo = 0; // ya lo tenemos declarado y asignado for (; i < 3; i++) { // no es necesario "comenzar" alerta (yo); // 0, 1, 2 }
También podemos eliminar la parte step
:
sea yo = 0; para (; yo < 3;) { alerta( i++ ); }
Esto hace que el bucle sea idéntico a while (i < 3)
.
De hecho, podemos eliminar todo, creando un bucle infinito:
para (;;) { // se repite sin límites }
Tenga en cuenta que los dos for
punto y coma ;
debe estar presente. De lo contrario, se produciría un error de sintaxis.
Normalmente, un bucle sale cuando su condición se vuelve falsa.
Pero podemos forzar la salida en cualquier momento usando la directiva especial break
.
Por ejemplo, el siguiente bucle solicita al usuario una serie de números, "rompiéndose" cuando no se ingresa ningún número:
sea suma = 0; mientras (verdadero) { let value = +prompt("Ingrese un número", ''); si (!valor) se rompe; // (*) suma += valor; } alerta( 'Suma: ' + suma );
La directiva break
se activa en la línea (*)
si el usuario ingresa una línea vacía o cancela la entrada. Detiene el bucle inmediatamente, pasando el control a la primera línea después del bucle. Es decir, alert
.
La combinación “bucle infinito + break
según sea necesario” es ideal para situaciones en las que el estado de un bucle debe comprobarse no al principio o al final del mismo, sino en el medio o incluso en varios lugares de su cuerpo.
La directiva continue
es una “versión más ligera” de break
. No detiene todo el ciclo. En cambio, detiene la iteración actual y obliga al bucle a iniciar una nueva (si la condición lo permite).
Podemos usarlo si terminamos con la iteración actual y nos gustaría pasar a la siguiente.
El siguiente bucle continue
generando solo valores impares:
para (sea i = 0; i < 10; i++) { // si es cierto, omite la parte restante del cuerpo si (i % 2 == 0) continuar; alerta(yo); // 1, luego 3, 5, 7, 9 }
Para valores pares de i
, la directiva continue
deja de ejecutar el cuerpo y pasa el control a la siguiente iteración de for
(con el siguiente número). Por lo tanto, la alert
solo se llama para valores impares.
La directiva continue
ayuda a disminuir el anidamiento.
Un bucle que muestra valores impares podría verse así:
para (sea i = 0; i < 10; i++) { si (yo % 2) { alerta (yo); } }
Desde un punto de vista técnico, esto es idéntico al ejemplo anterior. Seguramente, podemos simplemente envolver el código en un bloque if
en lugar de usar continue
.
Pero como efecto secundario, esto creó un nivel más de anidamiento (la llamada alert
dentro de las llaves). Si el código dentro de if
tiene más de unas pocas líneas, eso puede disminuir la legibilidad general.
Sin break/continue
hacia el lado derecho de '?'
Tenga en cuenta que las construcciones de sintaxis que no son expresiones no se pueden utilizar con el operador ternario ?
. En particular, allí no se permiten directivas como break/continue
.
Por ejemplo, si tomamos este código:
si (yo > 5) { alerta(yo); } demás { continuar; }
…y reescríbelo usando un signo de interrogación:
(yo > 5)? alerta(i): continuar; // continuar no está permitido aquí
…deja de funcionar: hay un error de sintaxis.
¿Ésta es sólo otra razón para no utilizar el operador de signo de interrogación ?
en lugar de if
.
A veces necesitamos salir de varios bucles anidados a la vez.
Por ejemplo, en el siguiente código recorremos i
y j
, solicitando las coordenadas (i, j)
de (0,0)
a (2,2)
:
para (sea i = 0; i < 3; i++) { para (sea j = 0; j < 3; j++) { let input = Prompt(`Valor en las coordenadas (${i},${j})`, ''); // ¿Qué pasa si queremos salir de aquí a Listo (abajo)? } } alerta('¡Listo!');
Necesitamos una forma de detener el proceso si el usuario cancela la entrada.
La break
ordinaria después de input
solo rompería el bucle interno. Eso no es suficiente: ¡las etiquetas, vengan al rescate!
Una etiqueta es un identificador con dos puntos antes de un bucle:
nombre de etiqueta: para (...) { ... }
La instrucción break <labelName>
en el bucle siguiente se desglosa en la etiqueta:
exterior: for (sea i = 0; i < 3; i++) { para (sea j = 0; j < 3; j++) { let input = Prompt(`Valor en las coordenadas (${i},${j})`, ''); // si es una cadena vacía o cancelada, entonces salimos de ambos bucles si (!entrada) rompe exterior; // (*) // hacer algo con el valor... } } alerta('¡Listo!');
En el código anterior, break outer
busca hacia arriba la etiqueta denominada outer
y sale de ese bucle.
Entonces el control va directamente de (*)
a alert('Done!')
.
También podemos mover la etiqueta a una línea separada:
exterior: para (sea i = 0; i < 3; i++) { ... }
La directiva continue
también se puede utilizar con una etiqueta. En este caso, la ejecución del código salta a la siguiente iteración del bucle etiquetado.
Las etiquetas no permiten “saltar” a ningún lado
Las etiquetas no nos permiten saltar a un lugar arbitrario del código.
Por ejemplo, es imposible hacer esto:
romper etiqueta; // salta a la etiqueta de abajo (no funciona) etiqueta: para (...)
Una directiva break
debe estar dentro de un bloque de código. Técnicamente, cualquier bloque de código etiquetado servirá, por ejemplo:
etiqueta: { //... romper etiqueta; // obras //... }
…Aunque, el 99,9% del tiempo de break
se utiliza dentro de bucles, como hemos visto en los ejemplos anteriores.
Una continue
sólo es posible desde dentro de un bucle.
Cubrimos 3 tipos de bucles:
while
: la condición se verifica antes de cada iteración.
do..while
: la condición se verifica después de cada iteración.
for (;;)
: la condición se verifica antes de cada iteración, hay configuraciones adicionales disponibles.
Para hacer un bucle “infinito”, normalmente se utiliza la construcción while(true)
. Un bucle así, como cualquier otro, puede detenerse con la directiva break
.
Si no queremos hacer nada en la iteración actual y queremos pasar a la siguiente, podemos usar la directiva continue
.
break/continue
etiquetas de soporte antes del bucle. Una etiqueta es la única forma de break/continue
para escapar de un bucle anidado para ir a uno externo.
importancia: 3
¿Cuál es el último valor alertado por este código? ¿Por qué?
sea yo = 3; mientras (yo) { alerta (yo--); }
La respuesta: 1
.
sea yo = 3; mientras (yo) { alerta (yo--); }
Cada iteración del bucle disminuye i
en 1
. La verificación while(i)
detiene el ciclo cuando i = 0
.
Por tanto, los pasos del bucle forman la siguiente secuencia (“bucle desenrollado”):
sea yo = 3; alerta(yo--); // muestra 3, disminuye i a 2 alert(i--) // muestra 2, disminuye i a 1 alert(i--) // muestra 1, disminuye i a 0 // hecho, while(i) check detiene el ciclo
importancia: 4
Para cada iteración del bucle, escriba qué valor genera y luego compárelo con la solución.
Ambos bucles alert
de los mismos valores, ¿o no?
La forma del prefijo ++i
:
sea yo = 0; mientras (++i < 5) alerta (i);
La forma de sufijo i++
sea yo = 0; mientras (i++ < 5) alerta (i);
La tarea demuestra cómo las formas de sufijo/prefijo pueden conducir a resultados diferentes cuando se utilizan en comparaciones.
Del 1 al 4
sea yo = 0; mientras (++i < 5) alerta (i);
El primer valor es i = 1
, porque ++i
primero incrementa i
y luego devuelve el nuevo valor. Entonces la primera comparación es 1 < 5
y la alert
muestra 1
.
Luego sigue 2, 3, 4…
– los valores aparecen uno tras otro. La comparación siempre usa el valor incrementado, porque ++
está antes de la variable.
Finalmente, i = 4
se incrementa a 5
, la comparación while(5 < 5)
falla y el ciclo se detiene. Entonces 5
no se muestra.
Del 1 al 5
sea yo = 0; mientras (i++ < 5) alerta (i);
El primer valor es nuevamente i = 1
. La forma de sufijo de i++
incrementa i
y luego devuelve el valor anterior , por lo que la comparación i++ < 5
usará i = 0
(al contrario de ++i < 5
).
Pero la llamada alert
es aparte. Es otra declaración que se ejecuta después del incremento y la comparación. Entonces obtiene la corriente i = 1
.
Luego sigue 2, 3, 4…
Detengámonos en i = 4
. La forma del prefijo ++i
lo incrementaría y usaría 5
en la comparación. Pero aquí tenemos la forma de sufijo i++
. Entonces incrementa i
a 5
, pero devuelve el valor anterior. Por lo tanto, la comparación es en realidad while(4 < 5)
– verdadera, y el control pasa a alert
.
El valor i = 5
es el último, porque en el siguiente paso while(5 < 5)
es falso.
importancia: 4
Para cada bucle escriba qué valores va a mostrar. Luego compárelo con la respuesta.
¿Ambos bucles alert
los mismos valores o no?
La forma de sufijo:
for (sea i = 0; i < 5; i++) alerta( i );
La forma del prefijo:
para (sea i = 0; i < 5; ++i) alerta( i );
La respuesta: de 0
a 4
en ambos casos.
para (sea i = 0; i < 5; ++i) alerta( i ); for (sea i = 0; i < 5; i++) alerta( i );
Esto se puede deducir fácilmente del algoritmo de for
:
Ejecutar una vez i = 0
antes de todo (comenzar).
Verifique la condición i < 5
Si es true
, ejecute la alert(i)
y luego i++
El incremento i++
está separado de la verificación de condición (2). Esa es sólo otra declaración.
El valor devuelto por el incremento no se utiliza aquí, por lo que no hay diferencia entre i++
y ++i
.
importancia: 5
Utilice el bucle for
para generar números pares del 2
al 10
.
Ejecute la demostración
para (sea i = 2; i <= 10; i++) { si (yo % 2 == 0) { alerta (yo); } }
Usamos el operador de “módulo” %
para obtener el resto y verificar la uniformidad aquí.
importancia: 5
Vuelva a escribir el código cambiando el for
for a while
sin alterar su comportamiento (el resultado debe permanecer igual).
para (sea i = 0; i < 3; i++) { alerta(`número ${i}!`); }
sea yo = 0; mientras (yo < 3) { alerta(`número ${i}!`); yo ++; }
importancia: 5
Escribe un bucle que solicite un número mayor que 100
. Si el visitante ingresa otro número, pídale que lo ingrese nuevamente.
El bucle debe solicitar un número hasta que el visitante ingrese un número mayor que 100
o cancele la entrada/ingrese una línea vacía.
Aquí podemos asumir que el visitante sólo ingresa números. No es necesario implementar un manejo especial para una entrada no numérica en esta tarea.
Ejecute la demostración
deja número; hacer { num = Prompt("¿Ingrese un número mayor que 100?", 0); } mientras (núm <= 100 && núm);
El bucle do..while
.. while se repite mientras ambas comprobaciones sean verdaderas:
La verificación de num <= 100
, es decir, el valor ingresado aún no es mayor que 100
.
La verificación && num
es falsa cuando num
es null
o una cadena vacía. Entonces el ciclo while
también se detiene.
PD: Si num
es null
, entonces num <= 100
es true
, por lo que sin la segunda verificación el bucle no se detendría si el usuario hace clic en CANCELAR. Se requieren ambos controles.
importancia: 3
Un número entero mayor que 1
se llama primo si no se puede dividir sin resto por nada excepto 1
y sí mismo.
En otras palabras, n > 1
es primo si no se puede dividir uniformemente por nada excepto 1
y n
.
Por ejemplo, 5
es primo, porque no se puede dividir sin resto entre 2
, 3
y 4
.
Escriba el código que genera números primos en el intervalo de 2
a n
.
Para n = 10
el resultado será 2,3,5,7
.
PD: El código debería funcionar para cualquier n
, no estar ajustado para ningún valor fijo.
Hay muchos algoritmos para esta tarea.
Usemos un bucle anidado:
Para cada i en el intervalo { comprobar si tengo un divisor de 1..i en caso afirmativo => el valor no es primo si no => el valor es primo, muéstralo }
El código usando una etiqueta:
sea n = 10; siguientePrime: for (let i = 2; i <= n; i++) { // para cada i... for (let j = 2; j < i; j++) { // busca un divisor.. si (i % j == 0) continúa con nextPrime; // no es primo, ve al siguiente i } alerta (yo); // un primo }
Hay mucho espacio para optimizarlo. Por ejemplo, podríamos buscar los divisores de 2
a la raíz cuadrada de i
. Pero de todos modos, si queremos ser realmente eficientes para intervalos grandes, necesitamos cambiar el enfoque y confiar en matemáticas avanzadas y algoritmos complejos como tamiz cuadrático, tamiz de campo numérico general, etc.