La manière typique de boucler les x
temps en JavaScript est:
for (var i = 0; i < x; i++)
doStuff(i);
Mais je ne veux pas utiliser l' ++
opérateur ou avoir des variables mutables du tout. Y a-t-il donc un moyen, dans ES6, de boucler les x
temps d'une autre manière? J'adore le mécanisme de Ruby:
x.times do |i|
do_stuff(i)
end
Quelque chose de similaire dans JavaScript / ES6? Je pourrais en quelque sorte tricher et créer mon propre générateur:
function* times(x) {
for (var i = 0; i < x; i++)
yield i;
}
for (var i of times(5)) {
console.log(i);
}
Bien sûr, j'utilise toujours i++
. Au moins, c'est hors de vue :), mais j'espère qu'il y a un meilleur mécanisme dans ES6.
Réponses:
D'ACCORD!
Le code ci-dessous est écrit en utilisant les syntaxes ES6 mais pourrait tout aussi bien être écrit en ES5 ou même moins. ES6 n'est pas une exigence pour créer un "mécanisme pour boucler x fois"
Si vous n'avez pas besoin de l'itérateur dans le rappel , c'est l'implémentation la plus simple
Si vous avez besoin de l'itérateur , vous pouvez utiliser une fonction interne nommée avec un paramètre de compteur pour itérer pour vous
Mais quelque chose devrait se sentir mal à propos de ceux-ci ...
if
déclarations de branche unique sont laides - que se passe-t-il sur l'autre branche?undefined
- indication d'une fonction impure à effet secondaire"N'y a-t-il pas un meilleur moyen?"
Il y a. Revoyons d'abord notre implémentation initiale
Bien sûr, c'est simple, mais remarquez comment nous appelons
f()
et ne faisons rien avec. Cela limite vraiment le type de fonction que nous pouvons répéter plusieurs fois. Même si nous avons l'itérateur disponible, cef(i)
n'est pas beaucoup plus polyvalent.Et si nous commençons avec un meilleur type de procédure de répétition de fonction? Peut-être quelque chose qui fait un meilleur usage des entrées et des sorties.
Répétition de fonction générique
Ci-dessus, nous avons défini une
repeat
fonction générique qui prend une entrée supplémentaire qui est utilisée pour démarrer l'application répétée d'une seule fonction.Mettre
times
en œuvre avecrepeat
Eh bien, c'est facile maintenant; presque tout le travail est déjà fait.
Puisque notre fonction prend
i
comme entrée et retournei + 1
, cela fonctionne effectivement comme notre itérateur auquel nous passonsf
chaque fois.Nous avons également corrigé notre liste de problèmes
if
déclarations deundefined
Opérateur de virgule JavaScript, le
Au cas où vous auriez du mal à voir comment le dernier exemple fonctionne, cela dépend de votre connaissance de l'un des plus anciens axes de combat de JavaScript; l' opérateur virgule - en bref, il évalue les expressions de gauche à droite et renvoie la valeur de la dernière expression évaluée
Dans notre exemple ci-dessus, j'utilise
qui n'est qu'une manière succincte d'écrire
Optimisation des appels de queue
Aussi sexy que soient les implémentations récursives, à ce stade, il serait irresponsable de ma part de les recommander étant donné qu'aucune VM JavaScript à laquelle je pense ne prend en charge l'élimination correcte des appels de queue - babel avait l'habitude de la transpiler, mais elle a été "cassée; sera réimplémentée "statut depuis plus d'un an.
À ce titre, nous devrions revoir notre mise en œuvre de
repeat
pour la rendre sûre.Le code ci - dessous utilise des variables mutables
n
etx
notez que toutes les mutations sont localisées dans larepeat
fonction - aucun changement d'état (mutation) n'est visible de l'extérieur de la fonctionVous serez nombreux à dire "mais ce n'est pas fonctionnel!" - Je sais, détends-toi. Nous pouvons implémenter une interface
loop
/ style Clojurerecur
pour la boucle d'espace constant en utilisant des expressions pures ; rien de toutwhile
ça.Ici, nous résumons
while
notreloop
fonction - elle recherche unrecur
type spécial pour maintenir la boucle en cours d'exécution. Lorsqu'un non-recur
type est rencontré, la boucle est terminée et le résultat du calcul est retournéla source
g => g(g)(x)
). Y a-t-il un avantage à une fonction d'ordre supérieur par rapport à une fonction de premier ordre, comme dans ma solution?Utilisation de l' opérateur Spread ES2015 :
[...Array(n)].map()
Ou si vous n'avez pas besoin du résultat:
Ou en utilisant l' opérateur ES2015 Array.from :
Array.from(...)
Notez que si vous avez juste besoin d'une chaîne répétée, vous pouvez utiliser String.prototype.repeat .
la source
Array.from(Array(10), (_, i) => i*10)
[...Array(10)].forEach(() => console.log('looping 10 times');
la source
Array
clés sont utilisées.[0..x]
dans JS plus concis que dans ma réponse.Array.prototype.keys
etObject.prototype.keys
, mais c'est certainement déroutant à première vue.Je pense que la meilleure solution est d'utiliser
let
:Cela créera une nouvelle
i
variable (mutable) pour chaque évaluation de corps et assurera que lei
n'est changé que dans l'expression d'incrémentation dans cette syntaxe de boucle, pas de n'importe où ailleurs.Cela devrait suffire à mon humble avis. Même dans les langages purs, toutes les opérations (ou du moins, leurs interpréteurs) sont construites à partir de primitives qui utilisent la mutation. Tant qu'il est correctement défini, je ne vois pas ce qui ne va pas avec cela.
Tu devrais être bien avec
Ensuite, votre seul choix est d'utiliser la récursivité. Vous pouvez également définir cette fonction de générateur sans mutable
i
:Mais cela me semble exagéré et pourrait avoir des problèmes de performances (car l'élimination des appels de queue n'est pas disponible pour
return yield*
).la source
Cet extrait sera
console.log
test
4 fois.la source
Je pense que c'est assez simple:
ou
la source
Réponse: 9 décembre 2015
Personnellement, j'ai trouvé la réponse acceptée à la fois concise (bonne) et laconique (mauvaise). Je comprends que cette déclaration peut être subjective, alors veuillez lire cette réponse et voir si vous êtes d'accord ou pas d'accord
L'exemple donné dans la question ressemblait à celui de Ruby:
L'exprimer dans JS en utilisant ci-dessous permettrait:
Voici le code:
C'est tout!
Exemple d'utilisation simple:
Sinon, en suivant les exemples de réponse acceptée:
Note d'accompagnement - Définition d'une fonction de plage
Une question similaire / connexe, qui utilise des constructions de code fondamentalement très similaires, pourrait être l'existence d'une fonction Range pratique dans JavaScript (de base), quelque chose de similaire à la fonction range du soulignement.
Créer un tableau avec n nombres, à partir de x
Souligner
ES2015
Quelques alternatives:
Démo utilisant n = 10, x = 1:
Dans un test rapide que j'ai effectué, chacun des éléments ci-dessus fonctionnant un million de fois chacun en utilisant notre solution et la fonction doStuff, la première approche (Array (n) .fill ()) s'est avérée légèrement plus rapide.
la source
Cette version satisfait à l'exigence d'immuabilité de l'OP. Pensez également à utiliser
reduce
au lieu demap
dépendre de votre cas d'utilisation.C'est également une option si cela ne vous dérange pas une petite mutation dans votre prototype.
Maintenant on peut faire ça
+1 à arcseldon pour la
.fill
suggestion.la source
Voici une autre bonne alternative:
De préférence, comme @Dave Morse l'a souligné dans les commentaires, vous pouvez également vous débarrasser de l'
map
appel, en utilisant le deuxième paramètre de laArray.from
fonction comme ceci:la source
Array.from
sur MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Array.from({ length: label.length }, (_, i) => (...))
cela évite de créer un tableau temporaire vide juste pour lancer un appel à la carte.Ce n'est pas quelque chose que j'enseignerais (ou que j'utiliserais jamais dans mon code), mais voici une solution digne de codegolf sans muter une variable, pas besoin d'ES6:
Plus une chose intéressante de preuve de concept qu'une réponse utile, vraiment.
la source
Array.apply(null, {length: 10})
pas justeArray(10)
?Je suis en retard à la fête, mais comme cette question revient souvent dans les résultats de recherche, je voudrais juste ajouter une solution que je considère comme la meilleure en termes de lisibilité sans être longue (ce qui est idéal pour toute base de code IMO) . Il mute, mais je ferais ce compromis pour les principes de KISS.
la source
times
variable à l'intérieur de la boucle. Cecountdown
serait peut -être un meilleur nom. Sinon, réponse la plus claire et la plus claire sur la page.Afaik, il n'y a pas de mécanisme dans ES6 similaire à la
times
méthode de Ruby . Mais vous pouvez éviter la mutation en utilisant la récursivité:Démo: http://jsbin.com/koyecovano/1/edit?js,console
la source
Si vous êtes prêt à utiliser une bibliothèque, il existe également un lodash
_.times
ou un soulignement_.times
:Notez que cela renvoie un tableau des résultats, donc c'est vraiment plus comme ce rubis:
la source
Dans le paradigme fonctionnel
repeat
est généralement une fonction récursive infinie. Pour l'utiliser, nous avons besoin d'une évaluation paresseuse ou d'un style de passage continu.Répétition de fonction évaluée paresseuse
J'utilise un thunk (une fonction sans arguments) pour obtenir une évaluation paresseuse en Javascript.
Répétition de fonction avec style de passage de continuation
CPS est un peu effrayant au début. Cependant, il suit toujours le même schéma: Le dernier argument est la continuation (fonction), qui invoque son propre corps:
k => k(...)
. Veuillez noter que CPS transforme l'application à l'envers, c'est-à-diretake(8) (repeat...)
devientk(take(8)) (...)
là oùk
est partiellement appliquéerepeat
.Conclusion
En séparant la répétition (
repeat
) de la condition de terminaison (take
), nous gagnons en flexibilité - séparation des préoccupations jusqu'à sa fin amère: Dla source
Avantages de cette solution
Inconvénients - Mutation. Étant seulement interne, je m'en fiche, peut-être que d'autres non plus.
Exemples et code
Version TypeScipt
https://codepen.io/whitneyland/pen/aVjaaE?editors=0011
la source
aborder l'aspect fonctionnel:
la source
i
. Quelle est la raison de même utilisertimes
plus de vieuxfor
alors?var twice = times(2);
.for
deux fois?i++
. Il n'est pas évident de savoir comment intégrer quelque chose d'inacceptable dans une fonction l'améliore.Générateurs? La récursivité? Pourquoi tant de haine sur la mutation? ;-)
Si c'est acceptable tant que nous le «cachons», alors acceptez simplement l'utilisation d'un opérateur unaire et nous pouvons garder les choses simples :
Tout comme dans le rubis:
la source
J'ai enveloppé la réponse de @Tieme avec une fonction d'assistance.
Dans TypeScript:
Vous pouvez maintenant exécuter:
la source
J'ai fabriqué ça:
Usage:
La
i
variable renvoie le nombre de fois qu'elle a bouclé - utile si vous avez besoin de précharger une quantité x d'images.la source