La forma típica de xtiempos de bucle en JavaScript es:
for (var i = 0; i < x; i++)
doStuff(i);
Pero no quiero usar el ++operador o tener ninguna variable mutable en absoluto. Entonces, ¿hay alguna manera, en ES6, de recorrer los xtiempos de otra manera? Me encanta el mecanismo de Ruby:
x.times do |i|
do_stuff(i)
end
¿Algo similar en JavaScript / ES6? Podría hacer trampa y hacer mi propio generador:
function* times(x) {
for (var i = 0; i < x; i++)
yield i;
}
for (var i of times(5)) {
console.log(i);
}
Por supuesto que todavía estoy usando i++. Al menos está fuera de la vista :), pero espero que haya un mejor mecanismo en ES6.

Respuestas:
¡OKAY!
El siguiente código está escrito con sintaxis de ES6 pero podría escribirse con la misma facilidad en ES5 o incluso menos. ES6 no es un requisito para crear un "mecanismo para repetir x veces"
Si no necesita el iterador en la devolución de llamada , esta es la implementación más simple
Si necesita el iterador , puede usar una función interna con nombre con un parámetro de contador para iterar por usted
Pero algo debería sentirse mal sobre esos ...
ifdeclaraciones de una sola rama son feas, ¿qué sucede en la otra rama?undefined- indicación de función impura, efecto secundario"¿No hay una mejor manera?"
Ahi esta. Primero revisemos nuestra implementación inicial
Claro, es simple, pero observe cómo simplemente llamamos
f()y no hacemos nada con él. Esto realmente limita el tipo de función que podemos repetir varias veces. Incluso si tenemos el iterador disponible,f(i)no es mucho más versátil.¿Qué pasa si comenzamos con un mejor tipo de procedimiento de repetición de funciones? Quizás algo que haga un mejor uso de la entrada y la salida.
Función genérica de repetición
Arriba, definimos una
repeatfunción genérica que toma una entrada adicional que se utiliza para iniciar la aplicación repetida de una sola función.Implementando
timesconrepeatBueno, esto es fácil ahora; Casi todo el trabajo ya está hecho.
Dado que nuestra función toma
icomo entrada y regresai + 1, esto efectivamente funciona como nuestro iterador al que pasamosfcada vez.También hemos solucionado nuestra lista de problemas.
ifdeclaraciones feas de una sola ramaundefinedOperador de coma JavaScript, el
En caso de que tenga problemas para ver cómo funciona el último ejemplo, depende de su conocimiento de uno de los ejes de batalla más antiguos de JavaScript; el operador de coma : en resumen, evalúa las expresiones de izquierda a derecha y devuelve el valor de la última expresión evaluada
En nuestro ejemplo anterior, estoy usando
que es solo una forma sucinta de escribir
Tail Call Optimization
A pesar de lo sexy que son las implementaciones recursivas, en este punto sería irresponsable para mí recomendarlas, dado que ninguna máquina virtual JavaScript en la que pueda pensar admite la eliminación adecuada de llamadas de cola: Babel solía transpilarla, pero se ha roto, se volverá a implementar "estado por más de un año.
Como tal, deberíamos revisar nuestra implementación de
repeatpara que sea apilable.El código siguiente hace utilizar variables mutables
nyxpero tenga en cuenta que todas las mutaciones se localizan en larepeatfunción - no hay cambios de estado (mutaciones) son visibles desde fuera de la funciónEsto hará que muchos de ustedes digan "¡pero eso no es funcional!" - Lo sé, solo relájate. Podemos implementar una interfaz
loop/ estilo Clojurerecurpara bucles de espacio constante usando expresiones puras ; Ninguna de esaswhilecosas.Aquí abstraemos
whilenuestraloopfunción: busca unrecurtipo especial para mantener el ciclo en ejecución. Cuandorecurse encuentra un no tipo, el ciclo finaliza y se devuelve el resultado del cálculo.fuente
g => g(g)(x)). ¿Hay algún beneficio de una función de orden superior sobre una de primer orden, como en mi solución?Usando el operador ES2015 Spread :
[...Array(n)].map()O si no necesitas el resultado:
O utilizando el operador ES2015 Array.from :
Array.from(...)Tenga en cuenta que si solo necesita una cadena repetida, puede usar String.prototype.repeat .
fuente
Array.from(Array(10), (_, i) => i*10)[...Array(10)].forEach(() => console.log('looping 10 times');fuente
Arrayse usan las teclas.[0..x]haskell en JS más conciso que en mi respuesta.Array.prototype.keyseObject.prototype.keys, pero seguro que es confuso a primera vista.Creo que la mejor solución es usar
let:Eso creará una nueva
ivariable (mutable) para cada evaluación del cuerpo y asegura queisolo se cambie en la expresión de incremento en esa sintaxis de bucle, no en ningún otro lugar.Eso debería ser suficiente imo. Incluso en lenguajes puros, todas las operaciones (o al menos, sus intérpretes) se construyen a partir de primitivas que usan mutación. Mientras tenga el alcance adecuado, no puedo ver qué hay de malo en eso.
Deberías estar bien con
Entonces su única opción es usar la recursividad. Puede definir esa función del generador sin un mutable
itambién:Pero eso me parece excesivo y podría tener problemas de rendimiento (ya que la eliminación de llamadas de cola no está disponible
return yield*).fuente
Este fragmento lo hará
console.logtest4 veces.fuente
Creo que es bastante simple:
o
fuente
Respuesta: 09 de diciembre de 2015
Personalmente, encontré la respuesta aceptada tanto concisa (buena) como concisa (mala). Apreciamos que esta declaración puede ser subjetiva, así que lea esta respuesta y vea si está de acuerdo o en desacuerdo
El ejemplo dado en la pregunta fue algo así como el de Ruby:
Expresar esto en JS usando a continuación permitiría:
Aquí está el código:
¡Eso es!
Ejemplo de uso simple:
Alternativamente, siguiendo los ejemplos de la respuesta aceptada:
Nota al margen: definición de una función de rango
Una pregunta similar / relacionada, que utiliza construcciones de código fundamentalmente muy similares, podría ser si existe una función de Rango conveniente en JavaScript (núcleo), algo similar a la función de rango de subrayado.
Crea una matriz con n números, comenzando desde x
Guion bajo
ES2015
Par de alternativas:
Demostración usando n = 10, x = 1:
En una prueba rápida que ejecuté, cada una de las anteriores se ejecutó un millón de veces utilizando nuestra solución y la función doStuff, el enfoque anterior (Array (n) .fill ()) resultó un poco más rápido.
fuente
Esta versión satisface los requisitos de OP para la inmutabilidad. También considere usar en
reducelugar demapdepender de su caso de uso.Esta también es una opción si no te importa un poco de mutación en tu prototipo.
Ahora podemos hacer esto
+1 a arcseldon por la
.fillsugerencia.fuente
Aquí hay otra buena alternativa:
Preferiblemente, como señaló @Dave Morse en los comentarios, también puede deshacerse de la
mapllamada, utilizando el segundo parámetro de laArray.fromfunción de la siguiente manera:fuente
Array.fromen MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Array.from({ length: label.length }, (_, i) => (...))esto ahorra la creación de una matriz temporal vacía solo para iniciar una llamada al mapa.No es algo que enseñe (o use en mi código), pero aquí hay una solución digna de codegolf sin mutar una variable, sin necesidad de ES6:
Más una cosa interesante de prueba de concepto que una respuesta útil, realmente.
fuente
Array.apply(null, {length: 10})ser justoArray(10)?Llego tarde a la fiesta, pero como esta pregunta aparece a menudo en los resultados de búsqueda, me gustaría agregar una solución que considero la mejor en términos de legibilidad sin ser larga (lo cual es ideal para cualquier IMO de base de código) . Muta, pero haría esa compensación por los principios de KISS.
fuente
timesvariable dentro del bucle. Quizáscountdownsería un mejor nombre. De lo contrario, la respuesta más clara y clara en la página.Afaik, no hay ningún mecanismo en ES6 similar al de Ruby
timesmétodo . Pero puede evitar la mutación usando la recursividad:Demostración: http://jsbin.com/koyecovano/1/edit?js,console
fuente
Si está dispuesto a usar una biblioteca, también hay lodash
_.timeso guión bajo_.times:Tenga en cuenta que esto devuelve una serie de resultados, por lo que es realmente más parecido a este rubí:
fuente
En el paradigma funcional
repeatsuele ser una función recursiva infinita. Para usarlo necesitamos una evaluación perezosa o un estilo de pase de continuación.Lazy evaluó la repetición de funciones
Utilizo un thunk (una función sin argumentos) para lograr una evaluación perezosa en Javascript.
Repetición de funciones con estilo de paso continuo
CPS da un poco de miedo al principio. Sin embargo, siempre sigue el mismo patrón: El último argumento es la continuación (una función), que invoca su propio cuerpo:
k => k(...). Tenga en cuenta que CPS convierte la aplicación al revés, es decir, setake(8) (repeat...)convierte enk(take(8)) (...)dóndekse aplica parcialmenterepeat.Conclusión
Al separar la repetición (
repeat) de la condición de terminación (take) obtenemos flexibilidad, separación de preocupaciones hasta su amargo final: Dfuente
Ventajas de esta solución
Desventajas - Mutación. Siendo interno solo no me importa, tal vez algunos otros tampoco.
Ejemplos y código
Versión TypeScipt
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011
fuente
abordando el aspecto funcional:
fuente
i. ¿Cuál es la razón para incluso usartimesmás de lo normalforentonces?var twice = times(2);.fordos veces?i++. No es obvio cómo envolver algo inaceptable en una función lo mejora.Generadores? Recursividad? ¿Por qué tanto odio a la mutatina? ;-)
Si es aceptable siempre que lo "ocultemos", simplemente acepte el uso de un operador unario y podemos simplificar las cosas :
Como en rubí:
fuente
Envolví la respuesta de @Tieme con una función auxiliar.
En TypeScript:
Ahora puedes ejecutar:
fuente
Yo hice esto:
Uso:
La
ivariable devuelve la cantidad de veces que se ha repetido, útil si necesita precargar una cantidad x de imágenes.fuente