Quelle est la méthode la meilleure ou la plus concise pour retourner une chaîne répétée un nombre arbitraire de fois?
Ce qui suit est mon meilleur coup jusqu'à présent:
function repeat(s, n){
var a = [];
while(a.length < n){
a.push(s);
}
return a.join('');
}
javascript
string
Brad
la source
la source
Réponses:
Je mettrais cette fonction directement sur l'objet String. Au lieu de créer un tableau, de le remplir et de le joindre avec un caractère vide, créez simplement un tableau de la bonne longueur et joignez-le à la chaîne souhaitée. Même résultat, moins de processus!
la source
String.repeat = function(string, num){ return new Array(parseInt(num) + 1).join(string); };
. Appelez ça comme ça:String.repeat('/\', 20)
J'ai testé les performances de toutes les approches proposées.
Voici la variante la plus rapide que j'ai.
Ou en tant que fonction autonome :
Il est basé sur l' algorithme artistoex . C'est vraiment rapide. Et
count
plus elle est grande , plus elle va vite par rapport à l'new Array(count + 1).join(string)
approche traditionnelle .Je n'ai changé que 2 choses:
pattern = this
parpattern = this.valueOf()
(efface une conversion de type évidente);if (count < 1)
vérification de prototypejs en haut de la fonction pour exclure les actions inutiles dans ce cas.UPD
Créé un petit terrain de jeu pour tester les performances ici pour ceux qui le souhaitent.
variable
count
~ 0 .. 100:constante
count
= 1024:Utilisez-le et rendez-le encore plus rapide si vous le pouvez :)
la source
count < 1
cas est vraiment une optimisation inutile.Ce problème est un problème d'optimisation bien connu / "classique" pour JavaScript, dû au fait que les chaînes JavaScript sont "immuables" et l'ajout par concaténation d'un seul caractère à une chaîne nécessite la création de, y compris l'allocation de mémoire et la copie vers , une toute nouvelle chaîne.
Malheureusement, la réponse acceptée sur cette page est fausse, où "faux" signifie par un facteur de performance de 3x pour les chaînes de caractères simples et de 8x-97x pour les chaînes courtes répétées plusieurs fois, à 300x pour les phrases répétitives, et infiniment incorrect lorsque prendre la limite des rapports de complexité des algorithmes comme
n
va à l'infini. En outre, il y a une autre réponse sur cette page qui est presque exacte (basée sur l'une des nombreuses générations et variations de la bonne solution qui a circulé sur Internet au cours des 13 dernières années). Cependant, cette solution "presque juste" manque un point clé de l'algorithme correct provoquant une dégradation des performances de 50%.Résultats de performance JS pour la réponse acceptée, l'autre réponse la plus performante (basée sur une version dégradée de l'algorithme d'origine dans cette réponse), et cette réponse en utilisant mon algorithme créé il y a 13 ans
~ Octobre 2000 J'ai publié un algorithme pour ce problème exact qui a été largement adapté, modifié, puis finalement mal compris et oublié. Pour remédier à ce problème, en août 2008, j'ai publié un article http://www.webreference.com/programming/javascript/jkm3/3.html expliquant l'algorithme et l'utilisant comme exemple de simples optimisations JavaScript à usage général. À ce jour, Web Reference a effacé mes coordonnées et même mon nom dans cet article. Et encore une fois, l'algorithme a été largement adapté, modifié, puis mal compris et largement oublié.
Dans les deux mois suivant la publication de cet article, cette même question a été publiée sur Stack Overflow et a volé sous mon radar jusqu'à présent, alors qu'apparemment l'algorithme d'origine pour ce problème a de nouveau été oublié. La meilleure solution disponible sur cette page Stack Overflow est une version modifiée de ma solution, éventuellement séparée par plusieurs générations. Malheureusement, les modifications ont ruiné l'optimalité de la solution. En fait, en changeant la structure de la boucle par rapport à mon original, la solution modifiée effectue une étape supplémentaire complètement inutile de la duplication exponentielle (joignant ainsi la plus grande chaîne utilisée dans la bonne réponse avec elle-même un temps supplémentaire, puis la rejetant).
Ci-dessous s'ensuit une discussion sur certaines optimisations JavaScript liées à toutes les réponses à ce problème et pour le bénéfice de tous.
Technique: éviter les références aux objets ou aux propriétés des objets
Pour illustrer le fonctionnement de cette technique, nous utilisons une fonction JavaScript réelle qui crée des chaînes de la longueur nécessaire. Et comme nous le verrons, plus d'optimisations peuvent être ajoutées!
Une fonction comme celle utilisée ici consiste à créer un remplissage pour aligner des colonnes de texte, pour formater de l'argent ou pour remplir des données de bloc jusqu'à la limite. Une fonction de génération de texte permet également une entrée de longueur variable pour tester toute autre fonction qui fonctionne sur du texte. Cette fonction est l'un des composants importants du module de traitement de texte JavaScript.
Au fur et à mesure, nous couvrirons deux autres techniques d'optimisation les plus importantes tout en développant le code original en un algorithme optimisé pour créer des chaînes. Le résultat final est une fonction de haute performance de niveau industriel que j'ai utilisée partout - alignant les prix et les totaux des articles dans les formulaires de commande JavaScript, le formatage des données et le formatage des e-mails / SMS et de nombreuses autres utilisations.
Code original pour créer des chaînes
stringFill1()
La syntaxe est ici est claire. Comme vous pouvez le voir, nous avons déjà utilisé des variables de fonction locales, avant de passer à d'autres optimisations.
Sachez qu'il existe une référence innocente à une propriété d'objet
s.length
dans le code qui nuit à ses performances. Pire encore, l'utilisation de cette propriété d'objet réduit la simplicité du programme en faisant l'hypothèse que le lecteur connaît les propriétés des objets chaîne JavaScript.L'utilisation de cette propriété d'objet détruit la généralité du programme informatique. Le programme suppose qu'il
x
doit s'agir d'une chaîne de longueur un. Cela limite l'application de lastringFill1()
fonction à tout sauf à la répétition de caractères uniques. Même des caractères uniques ne peuvent pas être utilisés s'ils contiennent plusieurs octets comme l'entité HTML
.Le pire problème causé par cette utilisation inutile d'une propriété d'objet est que la fonction crée une boucle infinie si elle est testée sur une chaîne d'entrée vide
x
. Pour vérifier la généralité, appliquez un programme à la plus petite quantité possible d'entrée. Un programme qui se bloque lorsqu'on lui demande de dépasser la quantité de mémoire disponible a une excuse. Un programme comme celui-ci qui se bloque lorsqu'on lui demande de ne rien produire est inacceptable. Parfois, un joli code est un code toxique.La simplicité peut être un objectif ambigu de la programmation informatique, mais ce n'est généralement pas le cas. Lorsqu'un programme n'a pas un niveau de généralité raisonnable, il n'est pas valable de dire: "Le programme est assez bon pour autant qu'il va". Comme vous pouvez le voir, l'utilisation de la
string.length
propriété empêche ce programme de fonctionner dans un paramètre général et, en fait, le programme incorrect est prêt à provoquer un blocage du navigateur ou du système.Existe-t-il un moyen d'améliorer les performances de ce JavaScript et de résoudre ces deux problèmes graves?
Bien sûr. Utilisez simplement des entiers.
Code optimisé pour créer des chaînes
stringFill2()
Code temporel à comparer
stringFill1()
etstringFill2()
Le succès jusqu'ici de
stringFill2()
stringFill1()
prend 47,297 microsecondes (millionièmes de seconde) pour remplir une chaîne de 100 octets etstringFill2()
27,68 microsecondes pour faire la même chose. Cela représente presque un doublement des performances en évitant une référence à une propriété d'objet.Technique: évitez d'ajouter des cordes courtes à des cordes longues
Notre résultat précédent semblait bon - très bon, en fait. La fonction améliorée
stringFill2()
est beaucoup plus rapide grâce à l'utilisation de nos deux premières optimisations. Le croiriez-vous si je vous disais qu'il peut être amélioré pour être beaucoup plus rapide qu'il ne l'est actuellement?Oui, nous pouvons atteindre cet objectif. À l'heure actuelle, nous devons expliquer comment nous évitons d'ajouter des chaînes courtes à des chaînes longues.
Le comportement à court terme semble assez bon par rapport à notre fonction d'origine. Les informaticiens aiment analyser le "comportement asymptotique" d'une fonction ou d'un algorithme de programme informatique, ce qui signifie étudier son comportement à long terme en le testant avec des entrées plus importantes. Parfois, sans faire d'autres tests, on ne prend jamais conscience des moyens d'améliorer un programme informatique. Pour voir ce qui va se passer, nous allons créer une chaîne de 200 octets.
Le problème qui apparaît avec
stringFill2()
En utilisant notre fonction de synchronisation, nous constatons que le temps augmente à 62,54 microsecondes pour une chaîne de 200 octets, contre 27,68 pour une chaîne de 100 octets. Il semble que le temps devrait être doublé pour faire deux fois plus de travail, mais il est plutôt triplé ou quadruplé. D'après l'expérience de programmation, ce résultat semble étrange, car la fonction devrait être légèrement plus rapide car le travail est effectué plus efficacement (200 octets par appel de fonction plutôt que 100 octets par appel de fonction). Ce problème est lié à une propriété insidieuse des chaînes JavaScript: les chaînes JavaScript sont "immuables".
Immuable signifie que vous ne pouvez pas modifier une chaîne une fois qu'elle a été créée. En ajoutant un octet à la fois, nous n'utilisons pas un octet supplémentaire d'effort. Nous recréons en fait la chaîne entière plus un octet de plus.
En effet, pour ajouter un octet de plus à une chaîne de 100 octets, il faut 101 octets de travail. Analysons brièvement le coût de calcul pour créer une chaîne d'
N
octets. Le coût de l'ajout du premier octet est de 1 unité d'effort de calcul. Le coût de l'ajout du deuxième octet n'est pas d'une unité mais de 2 unités (copie du premier octet dans un nouvel objet chaîne ainsi que l'ajout du deuxième octet). Le troisième octet nécessite un coût de 3 unités, etc.C(N) = 1 + 2 + 3 + ... + N = N(N+1)/2 = O(N^2)
. Le symboleO(N^2)
est prononcé Big O de N au carré, et cela signifie que le coût de calcul à long terme est proportionnel au carré de la longueur de la chaîne. Pour créer 100 caractères, il faut 10 000 unités de travail et pour créer 200 caractères, il faut 40 000 unités de travail.C'est pourquoi il a fallu plus de deux fois plus de temps pour créer 200 caractères que 100 caractères. En fait, cela aurait dû prendre quatre fois plus de temps. Notre expérience de programmation était correcte dans la mesure où le travail est effectué un peu plus efficacement pour les chaînes plus longues, et donc cela n'a pris que trois fois plus de temps. Une fois que la surcharge de l'appel de fonction devient négligeable quant à la longueur d'une chaîne que nous créons, il faudra en fait quatre fois plus de temps pour créer une chaîne deux fois plus longtemps.
(Remarque historique: cette analyse ne s'applique pas nécessairement aux chaînes dans le code source, par exemple
html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n'
, puisque le compilateur de code source JavaScript peut réunir les chaînes avant de les transformer en un objet chaîne JavaScript. Il y a quelques années à peine, l'implémentation KJS de JavaScript se bloquait ou se bloquait lors du chargement de longues chaînes de code source jointes par des signes plus. Comme le temps de calculO(N^2)
n'était pas difficile de créer des pages Web qui surchargeaient le navigateur Web Konqueror ou Safari, qui utilisait le noyau du moteur JavaScript KJS. J'ai d'abord suis tombé sur ce problème lorsque je développais un langage de balisage et un analyseur de langage de balisage JavaScript, puis j'ai découvert la cause du problème lorsque j'ai écrit mon script pour les inclusions JavaScript.)De toute évidence, cette dégradation rapide des performances est un énorme problème. Comment pouvons-nous y faire face, étant donné que nous ne pouvons pas changer la façon dont JavaScript traite les chaînes comme des objets immuables? La solution consiste à utiliser un algorithme qui recrée la chaîne le moins de fois possible.
Pour clarifier, notre objectif est d'éviter d'ajouter des chaînes courtes à des chaînes longues, car pour ajouter la chaîne courte, la chaîne longue entière doit également être dupliquée.
Fonctionnement de l'algorithme pour éviter d'ajouter des chaînes courtes à des chaînes longues
Voici un bon moyen de réduire le nombre de fois où de nouveaux objets chaîne sont créés. Concaténer des longueurs de chaîne plus longues ensemble de sorte que plusieurs octets à la fois soient ajoutés à la sortie.
Par exemple, pour créer une chaîne de longueur
N = 9
:Pour ce faire, il fallait créer une chaîne de longueur 1, créer une chaîne de longueur 2, créer une chaîne de longueur 4, créer une chaîne de longueur 8 et enfin, créer une chaîne de longueur 9. Combien avons-nous économisé?
Ancien coût
C(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 9 = 45
.Nouveau coût
C(9) = 1 + 2 + 4 + 8 + 9 = 24
.Notez que nous devions ajouter une chaîne de longueur 1 à une chaîne de longueur 0, puis une chaîne de longueur 1 à une chaîne de longueur 1, puis une chaîne de longueur 2 à une chaîne de longueur 2, puis une chaîne de longueur 4 à une chaîne de longueur 4, puis une chaîne de longueur 8 à une chaîne de longueur 1, afin d'obtenir une chaîne de longueur 9. Ce que nous faisons peut être résumé en évitant d'ajouter des chaînes courtes à des chaînes longues, ou dans d'autres mots, en essayant de concaténer des chaînes de longueur égale ou presque égale.
Pour l'ancien coût de calcul, nous avons trouvé une formule
N(N+1)/2
. Existe-t-il une formule pour le nouveau coût? Oui, mais c'est compliqué. L'important est que ce soit le casO(N)
, et donc doubler la longueur de la chaîne doublera approximativement la quantité de travail plutôt que de la quadrupler.Le code qui implémente cette nouvelle idée est presque aussi compliqué que la formule du coût de calcul. Lorsque vous le lisez, n'oubliez pas que cela
>>= 1
signifie de décaler vers la droite d'un octet. Donc, sin = 10011
est un nombre binaire, alorsn >>= 1
résulte en la valeurn = 1001
.L'autre partie du code que vous pourriez ne pas reconnaître est l'écriture au niveau du bit et de l'opérateur
&
. L'expressionn & 1
évalue vrai si le dernier chiffre binaire den
est 1 et faux si le dernier chiffre binaire den
est 0.Nouveau très efficace
stringFill3()
fonctionIl semble laid à l'œil non averti, mais ses performances ne sont rien de moins que charmantes.
Voyons à quel point cette fonction fonctionne bien. Après avoir vu les résultats, il est probable que vous n'oublierez jamais la différence entre un
O(N^2)
algorithme et unO(N)
algorithme.stringFill1()
prend 88,7 microsecondes (millionièmes de seconde) pour créer une chaîne de 200 octets,stringFill2()
prend 62,54 etstringFill3()
ne prend que 4,608. Qu'est-ce qui a rendu cet algorithme tellement meilleur? Toutes les fonctions ont profité de l'utilisation de variables de fonction locales, mais en profitant des deuxième et troisième techniques d'optimisation, les performances ont été multipliées par vingtstringFill3()
.Analyse plus approfondie
Qu'est-ce qui fait que cette fonction particulière fait exploser la concurrence hors de l'eau?
Comme je l'ai mentionné, la raison pour laquelle ces deux fonctions
stringFill1()
etstringFill2()
s'exécutent si lentement est que les chaînes JavaScript sont immuables. La mémoire ne peut pas être réallouée pour permettre d'ajouter un octet de plus à la fois aux données de chaîne stockées par JavaScript. Chaque fois qu'un octet supplémentaire est ajouté à la fin de la chaîne, la chaîne entière est régénérée du début à la fin.Ainsi, afin d'améliorer les performances du script, il faut précalculer des chaînes de longueur plus longue en concaténant deux chaînes ensemble à l'avance, puis en construisant récursivement la longueur de chaîne souhaitée.
Par exemple, pour créer une chaîne d'octets de 16 lettres, une chaîne de deux octets serait d'abord précalculée. Ensuite, la chaîne de deux octets serait réutilisée pour précalculer une chaîne de quatre octets. Ensuite, la chaîne de quatre octets serait réutilisée pour précalculer une chaîne de huit octets. Enfin, deux chaînes de huit octets seraient réutilisées pour créer la nouvelle chaîne souhaitée de 16 octets. Au total, quatre nouvelles chaînes ont dû être créées, une de longueur 2, une de longueur 4, une de longueur 8 et une de longueur 16. Le coût total est de 2 + 4 + 8 + 16 = 30.
À long terme, cette efficacité peut être calculée en ajoutant dans l'ordre inverse et en utilisant une série géométrique commençant par un premier terme a1 = N et ayant un rapport commun de r = 1/2. La somme d'une série géométrique est donnée par
a_1 / (1-r) = 2N
.C'est plus efficace que d'ajouter un caractère pour créer une nouvelle chaîne de longueur 2, créant une nouvelle chaîne de longueur 3, 4, 5, etc. jusqu'à 16. L'algorithme précédent utilisait ce processus d'ajout d'un seul octet à la fois , et le coût total serait
n (n + 1) / 2 = 16 (17) / 2 = 8 (17) = 136
.De toute évidence, 136 est un nombre beaucoup plus grand que 30, et donc l'algorithme précédent prend beaucoup, beaucoup plus de temps pour construire une chaîne.
Pour comparer les deux méthodes, vous pouvez voir à quel point l'algorithme récursif (également appelé "diviser pour mieux régner") est plus rapide sur une chaîne de longueur 123 457. Sur mon ordinateur FreeBSD, cet algorithme, implémenté dans la
stringFill3()
fonction, crée la chaîne en 0,001058 secondes, tandis que lastringFill1()
fonction d' origine crée la chaîne en 0,0808 secondes. La nouvelle fonction est 76 fois plus rapide.La différence de performances augmente à mesure que la longueur de la chaîne augmente. Dans la limite où des chaînes de plus en plus grandes sont créées, la fonction d'origine se comporte à peu près comme des temps
C1
(constants)N^2
et la nouvelle fonction se comporte comme des tempsC2
(constants)N
.À partir de notre expérience, nous pouvons déterminer la valeur de l'
C1
êtreC1 = 0.0808 / (123457)2 = .00000000000530126997
et la valeur de l'C2
êtreC2 = 0.001058 / 123457 = .00000000856978543136
. En 10 secondes, la nouvelle fonction pourrait créer une chaîne contenant 1 166 890 359 caractères. Pour créer cette même chaîne, l'ancienne fonction aurait besoin de 7 218 384 secondes.C'est près de trois mois contre dix secondes!
Je ne réponds (plusieurs années en retard) que parce que ma solution originale à ce problème flotte sur Internet depuis plus de 10 ans, et est apparemment encore mal comprise par quelques-uns qui s'en souviennent. Je pensais qu'en écrivant un article à ce sujet ici, j'aiderais:
Optimisations des performances pour JavaScript haute vitesse / Page 3
Malheureusement, certaines des autres solutions présentées ici sont encore certaines de celles qui prendraient trois mois pour produire la même quantité de sortie qu'une solution appropriée crée en 10 secondes.
Je veux prendre le temps de reproduire une partie de l'article ici comme une réponse canonique sur Stack Overflow.
Notez que l'algorithme le plus performant ici est clairement basé sur mon algorithme et a probablement été hérité de l'adaptation de 3e ou 4e génération de quelqu'un d'autre. Malheureusement, les modifications ont entraîné une réduction de ses performances. La variation de ma solution présentée ici n'a peut-être pas compris mon
for (;;)
expression déroutante qui ressemble à la boucle infinie principale d'un serveur écrit en C, et qui a été simplement conçue pour permettre une instruction de rupture soigneusement positionnée pour le contrôle de la boucle, le moyen le plus compact de éviter de répliquer de façon exponentielle la chaîne une fois de plus inutile.la source
Celui-ci est assez efficace
la source
Bonnes nouvelles!
String.prototype.repeat
fait maintenant partie de JavaScript .La méthode est prise en charge par tous les principaux navigateurs, à l'exception d'Internet Explorer et d'Android Webview. Pour une liste à jour, voir MDN: String.prototype.repeat> Compatibilité du navigateur .
MDN a un polyfill pour les navigateurs sans support.
la source
String.prototype.repeat est désormais ES6 Standard.
la source
Extension de la solution de P.Bailey :
De cette façon, vous devez être à l'abri des types d'arguments inattendus:
EDIT: Crédits à jerone pour son
++num
idée élégante !la source
String.prototype.repeat = function(n){return new Array(isNaN(n) ? 1 : ++n).join(this);}
Utilisation
Array(N+1).join("string_to_repeat")
la source
voici comment répéter la chaîne plusieurs fois à l'aide du délimètre.
la source
Voici une amélioration de 5 à 7% de la réponse de Disfated.
Déroulez la boucle en vous arrêtant sur
count > 1
et effectuez unresult += pattnern
concat supplémentaire après la boucle. Cela évitera les boucles finales précédemment inutiliséespattern += pattern
sans avoir à utiliser un if-check coûteux. Le résultat final ressemblerait à ceci:Et voici le violon de disfated fourchu pour la version déroulée: http://jsfiddle.net/wsdfg/
la source
la source
var r=s; for (var a=1;...
:)))) Quoi qu'il en soit, selon ce test ( jsperf.com/string-repeat/2 ), faire une boucle simple avec concaténation de chaînes comme ce que vous avez suggéré semble être beaucoup plus rapide sur Chrome que d'utiliser Array .joindre.Tests des différentes méthodes:
la source
Voici la version sûre de JSLint
la source
Pour tous les navigateurs
C'est à peu près aussi concis que possible:
Si vous vous souciez également de la performance, c'est une bien meilleure approche:
Si vous souhaitez comparer les performances des deux options, consultez ce violon et ce violon pour des tests de référence. Lors de mes propres tests, la deuxième option était environ 2 fois plus rapide dans Firefox et environ 4 fois plus rapide dans Chrome!
Pour les navigateurs modernes uniquement:
Dans les navigateurs modernes, vous pouvez désormais également procéder comme suit:
Cette option est non seulement plus courte que les deux autres options, mais elle est encore plus rapide que la deuxième option.
Malheureusement, cela ne fonctionne dans aucune version d'Internet Explorer. Les chiffres du tableau spécifient la première version du navigateur qui prend entièrement en charge la méthode:
la source
Vous pouvez le tester sur JSFiddle . Comparé au hacky
Array.join
et au mien est, en gros, 10 (Chrome) à 100 (Safari) à 200 (Firefox) fois plus rapide (selon le navigateur).la source
Juste une autre fonction de répétition:
la source
ES2015
a été réalisé cetterepeat()
méthode!http://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.repeat
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ Chaîne / répétition
http://www.w3schools.com/jsref/jsref_repeat.asp
la source
Ce peut être le plus petit récursif: -
la source
Violon: http://jsfiddle.net/3Y9v2/
la source
Concaténation récursive simple
Je voulais juste lui donner un coup bash, et j'ai fait ceci:
Je ne peux pas dire que j'y ai beaucoup réfléchi, et cela montre probablement :-)
C'est sans doute mieux
Et cela ressemble beaucoup à une réponse déjà publiée - je le sais.
Mais pourquoi être récursif?
Et que diriez-vous d'un petit comportement par défaut aussi?
Parce que, bien que la méthode non récursive gère des répétitions arbitrairement grandes sans atteindre les limites de la pile d'appels, elle est beaucoup plus lente.
Pourquoi ai-je pris la peine d'ajouter plus de méthodes qui ne sont pas à moitié aussi intelligentes que celles déjà publiées?
En partie pour mon propre amusement, et en partie pour souligner de la manière la plus simple, je sais qu'il existe de nombreuses façons d'écorcher un chat, et selon la situation, il est fort possible que la meilleure méthode apparemment ne soit pas idéale.
Une méthode relativement rapide et sophistiquée peut effectivement planter et brûler dans certaines circonstances, tandis qu'une méthode plus lente et plus simple peut faire le travail - éventuellement.
Certaines méthodes peuvent être un peu plus que des exploits, et en tant que telles sujettes à être corrigées de l'existence, et d'autres méthodes peuvent fonctionner à merveille dans toutes les conditions, mais sont si construites que l' on n'a tout simplement aucune idée de comment cela fonctionne.
"Et si je ne sais pas comment ça marche?!"
Sérieusement?
JavaScript souffre de l'une de ses plus grandes forces; il est très tolérant aux mauvais comportements, et si flexible qu'il se penchera en arrière pour retourner des résultats, alors qu'il aurait pu être mieux pour tout le monde s'il s'était cassé!
"Avec une grande puissance, vient une grande responsabilité" ;-)
Mais plus sérieusement et plus important encore, bien que des questions générales comme celle-ci conduisent à une impressionnant sous forme de réponses intelligentes qui, si rien d'autre, élargit ses connaissances et ses horizons, en fin de compte, la tâche à accomplir - le script pratique qui utilise la méthode résultante - peut nécessiter un peu moins, ou un peu plus intelligent que ce qui est suggéré.
Ces algorithmes «parfaits» sont amusants et tout, mais «taille unique» sera rarement, sinon jamais, meilleur que sur mesure.
Ce sermon vous a été présenté grâce à un manque de sommeil et à un intérêt passager. Allez-y et codez!
la source
Premièrement, les questions du PO semblent concerner la concision - que je comprends comme «simple et facile à lire», tandis que la plupart des réponses semblent concerner l'efficacité - ce qui n'est évidemment pas la même chose et je pense aussi que si vous ne des algorithmes spécifiques de manipulation de données volumineuses ne devraient pas vous inquiéter lorsque vous venez d'implémenter des fonctions Javascript de manipulation de données de base. La concision est beaucoup plus importante.
Deuxièmement, comme André Laszlo l'a noté, String.repeat fait partie d'ECMAScript 6 et est déjà disponible dans plusieurs implémentations populaires - donc l'implémentation la plus concise de
String.repeat
ne pas l'implémenter ;-)Enfin, si vous devez prendre en charge des hôtes qui n'offrent pas l'implémentation d'ECMAScript 6, le polyfill MDN mentionné par André Laszlo est tout sauf concis.
Donc, sans plus tarder - voici mon polyfill concis :
Oui, c'est une récursivité. J'aime les récursions - elles sont simples et si elles sont faites correctement, elles sont faciles à comprendre. En ce qui concerne l'efficacité, si la langue le prend en charge, elle peut être très efficace si elle est écrite correctement.
D'après mes tests, cette méthode est ~ 60% plus rapide que l'
Array.join
approche. Bien que cela ne soit manifestement pas du tout conforme à la mise en œuvre de Disfated, il est beaucoup plus simple que les deux.Ma configuration de test est le nœud v0.10, en utilisant le "mode strict" (je pense qu'il permet une sorte de TCO ), en appelant
repeat(1000)
une chaîne de 10 caractères un million de fois.la source
Si vous pensez que toutes ces définitions de prototypes, créations de tableaux et opérations de jointure sont excessives, utilisez simplement un code de ligne unique là où vous en avez besoin. La chaîne S se répète N fois:
la source
Array(N + 1).join(str)
méthode si ce n'est pas un goulot d'étranglement). S'il y a la moindre chance que vous l'utilisiez deux fois, déplacez-le vers une fonction correctement nommée.Utilisez Lodash pour la fonctionnalité de l'utilitaire Javascript, comme la répétition de chaînes.
Lodash offre de belles performances et une compatibilité ECMAScript.
Je le recommande vivement pour le développement de l'interface utilisateur et cela fonctionne bien côté serveur également.
Voici comment répéter la chaîne "yo" 2 fois en utilisant Lodash:
la source
Solution récursive utilisant diviser pour régner:
la source
Je suis venu ici au hasard et je n'ai jamais eu de raison de répéter un caractère en javascript auparavant.
J'ai été impressionné par la façon de faire de artistoex et par les résultats déçus. J'ai remarqué que le dernier enchaînement de chaînes n'était pas nécessaire, comme Dennis l'a également souligné.
J'ai remarqué un peu plus de choses en jouant avec l'échantillonnage déformé.
Les résultats variaient assez souvent, favorisant souvent la dernière manche et des algorithmes similaires se disputaient souvent la position. L'une des choses que j'ai changé était au lieu d'utiliser le compte généré par JSLitmus comme la graine pour les appels; comme le nombre a été généré différemment pour les différentes méthodes, j'ai mis un index. Cela a rendu la chose beaucoup plus fiable. J'ai ensuite vérifié si des chaînes de tailles différentes étaient transmises aux fonctions. Cela a empêché certaines des variations que j'ai vues, où certains algorithmes ont fait mieux au niveau des caractères uniques ou des chaînes plus petites. Cependant, les 3 meilleures méthodes ont toutes bien fonctionné quelle que soit la taille de la chaîne.
Kit de test fourchu
http://jsfiddle.net/schmide/fCqp3/134/
J'ai ensuite inclus le correctif de Dennis et décidé de voir si je pouvais trouver un moyen de sortir un peu plus.
Étant donné que javascript ne peut pas vraiment optimiser les choses, la meilleure façon d'améliorer les performances est d'éviter manuellement les choses. Si je retirais les 4 premiers résultats triviaux de la boucle, je pouvais éviter 2 à 4 magasins de chaînes et écrire le magasin final directement dans le résultat.
Cela a entraîné une amélioration de 1 à 2% en moyenne par rapport au correctif de Dennis. Cependant, différentes exécutions et différents navigateurs montreraient une variance assez juste pour que ce code supplémentaire ne vaille probablement pas l'effort par rapport aux 2 algorithmes précédents.
Un graphique
Edit: je l'ai fait principalement sous chrome. Firefox et IE favoriseront souvent Dennis d'un couple%.
la source
Méthode simple:
la source
Les gens compliquent cela de façon ridicule ou gâchent les performances. Des tableaux? Récursivité? Dis moi que c'est une blague.
Éditer. J'ai effectué des tests simples pour comparer avec la version au niveau du bit publiée par artistoex / disfated et un tas d'autres personnes. Ce dernier n'était que légèrement plus rapide, mais les ordres de grandeur étaient plus efficaces en mémoire. Pour 1000000 répétitions du mot «bla», le processus Node est passé à 46 mégaoctets avec l'algorithme de concaténation simple (ci-dessus), mais seulement 5,5 mégaoctets avec l'algorithme logarithmique. Ce dernier est certainement la voie à suivre. Le republier pour plus de clarté:
la source
string += string
moitié du temps redondante .Concaténation de chaînes basées sur un nombre.
J'espère que cela pourra aider!
la source
Avec ES8, vous pouvez également utiliser
padStart
oupadEnd
pour cela. par exemple.la source