Dizeyi Tekrarla - Javascript


271

Keyfi bir kez tekrarlanan bir dize döndürmek için en iyi veya en özlü yöntem nedir?

Şimdiye kadarki en iyi çekimim:

function repeat(s, n){
    var a = [];
    while(a.length < n){
        a.push(s);
    }
    return a.join('');
}

5
10 yıldan fazla bir süre önce bu soruna iyi bilinen bir çözüm vardı ve bu soruyu sormadan birkaç ay önce bir JavaScript optimizasyon makalesinde örnek olarak kullandım: webreference.com/programming/javascript/jkm3/3 .html Görünüşe göre, çoğu insan bu kodu unutmuş ve aşağıda benim gibi iyi olarak iyi olarak herhangi bir çözüm göremiyorum. En iyi algoritma kodumdan kaldırılmış gibi görünüyor; kodumun nasıl çalıştığının yanlış anlaşılması dışında, orijinalimde özel bir döngü ile ortadan kaldırılan üstel birleştirme bir adım daha yapar.
Joseph Myers

10
Kimse Joseph'in çözümünü kaldırmadı. Algoritma 3700 yaşında. Ek adımın maliyeti ihmal edilebilir. Ve bu makalede JavaScript dize birleştirme konusunda hataları ve yanlış anlamaları içeriyor. JavaScript gerçekten içten dizeleri nasıl işleyeceğini ilgilenen herkes için bkz Rope .
artistoex

4
Hiç kimse, en azından firefox'ta String protokol tekrarının tanımlandığını ve uygulandığını fark etmemiş görünüyor.
kennebec

3
@kennebec: Evet, bu soru sorulduğunda etrafta olmayan bir EcmaScript 6 özelliği. Şimdi oldukça iyi destekleniyor.
rvighne

3
@rvighne - Sadece şimdi kontrol kangax.github.io/compat-table/es6/#String.prototype.repeat ben "Oldukça iyi desteklenen" olarak münhasıran ateş destek ve krom düşünün olmaz
aaaaaa

Yanıtlar:


406

Yeni okuyucular için not: Bu cevap eski ve çok pratik değil - bu sadece "zekice" çünkü String şeyler yapmak için Array şeyler kullanır. "Daha az işlem" yazdığımda, kesinlikle "daha az kod" demek istedim, çünkü sonraki yanıtlarda belirttiği gibi, bir domuz gibi çalışır. Bu yüzden hız sizin için önemliyse kullanmayın.

Bu işlevi doğrudan String nesnesine koyardım. Bir dizi oluşturmak, doldurmak ve boş bir karakterle birleştirmek yerine, uygun uzunlukta bir dizi oluşturmak ve istediğiniz dizeyle birleştirmek yeterlidir. Aynı sonuç, daha az işlem!

String.prototype.repeat = function( num )
{
    return new Array( num + 1 ).join( this );
}

alert( "string to repeat\n".repeat( 4 ) );

36
Yerel nesneleri genişletmemeye çalışıyorum, aksi takdirde bu güzel bir çözüm. Teşekkürler!
brad

34
@ brad - neden olmasın? Küresel ad alanını, oldukça iyi tanımlanmış bir eve (String nesnesi) sahip bir işlevle kirletmeyi mi tercih edersiniz?
Peter Bailey

16
Aslında, argümanlarınızın her ikisi de global ad alanı için de geçerlidir. Eğer bir isim alanını genişletecek ve potansiyel çarpışmalara sahip olacaksam, bunu 1) global değil 2) ilgili olan ve 3) refactor kolay olur. Bu, global değil, String prototipine koymak anlamına gelir.
Peter Bailey

11
Bu işleve yapacağım bir değişiklik, sayısal bir dizeniz varsa, JS'nin tür hokkabazlığı nedeniyle garip davranışlar alabileceğiniz için parseInt () öğesini "num" etrafına koymak olacaktır. örneğin: "dizem" .repeat ("6") == "61"
nickf

19
Eğer yerli nesneleri uzatmak istemiyorum, bu gibi yerine dize nesne üzerinde işlev bırakabilir: String.repeat = function(string, num){ return new Array(parseInt(num) + 1).join(string); };. String.repeat('/\', 20)
Şöyle söyle

204

Önerilen tüm yaklaşımların performansını test ettim.

İşte sahip olduğum en hızlı varyant .

String.prototype.repeat = function(count) {
    if (count < 1) return '';
    var result = '', pattern = this.valueOf();
    while (count > 1) {
        if (count & 1) result += pattern;
        count >>= 1, pattern += pattern;
    }
    return result + pattern;
};

Veya tek başına işlev olarak:

function repeat(pattern, count) {
    if (count < 1) return '';
    var result = '';
    while (count > 1) {
        if (count & 1) result += pattern;
        count >>= 1, pattern += pattern;
    }
    return result + pattern;
}

Artistoex algoritmasına dayanır . Gerçekten hızlı. Ve ne kadar büyük olursa count, geleneksel new Array(count + 1).join(string)yaklaşımla karşılaştırıldığında o kadar hızlı gider .

Sadece 2 şeyi değiştirdim:

  1. ikame pattern = thisile pattern = this.valueOf()(temizler bir belirgin tipi dönüşümü);
  2. bu durumda gereksiz eylemleri hariç tutmak için prototypejs'den işlevin üstüne if (count < 1)kontrol ekledi .
  3. Dennis yanıtından uygulanan optimizasyon (% 5-7 hızlanma)

UPD

İlgilenenler için burada küçük bir performans testi oyun alanı oluşturduk.

değişken count~ 0 .. 100:

Performans diyagramı

sabit count= 1024:

Performans diyagramı

Kullanın ve mümkünse daha hızlı yapın :)


4
İyi iş! count < 1Durumun gerçekten gereksiz bir optimizasyon olduğunu düşünüyorum .
JayVee

Mükemmel algoritma O (log N).
ValueOf

2
Görüntü bağlantıları öldü.
Benjamin Gruenbaum

Bağlantılar iyi. Geçici olarak kullanılamıyor olabilir
14:57

JSFiddle testi artık doğru şekilde çalışmıyor; sadece ilk işlevi tekrar tekrar çalıştırmaya devam ediyor gibi görünüyor (emin olmak için yarım saat çalışmasını bıraktı)
RevanProdigalKnight

47

Bu sorun, JavaScript dizelerinin "değiştirilemez" olmasından ve JavaScript için tek bir karakterin bile birleştirilmesiyle eklenmesinin bilinen / "klasik" bir optimizasyon sorunudur. , tamamen yeni bir dize.

Ne yazık ki, bu sayfadaki kabul edilen cevap yanlıştır, burada "yanlış", basit tek karakterli dizeler için 3x ve daha fazla tekrarlanan kısa dizeler için 8x-97x, tekrarlanan cümleler için 300x'e ve tekrarlandığında sonsuz yanlış anlamına gelir. nsonsuzluğa giderken algoritmaların karmaşıklık oranlarının sınırını almak . Ayrıca, bu sayfada neredeyse doğru olan başka bir cevap daha var (son 13 yıl içinde internette dolaşan birçok neslin ve doğru çözümün varyasyonlarından birine dayanarak). Ancak bu "neredeyse doğru" çözüm, doğru algoritmanın% 50 performans düşüşüne neden olan kilit noktasını kaçırmaktadır.

JS Performance Kabul edilen yanıt, en iyi performans gösteren diğer yanıt (bu yanıttaki orijinal algoritmanın bozulmuş sürümüne dayanarak) ve 13 yıl önce oluşturulan algoritmamı kullanarak bu yanıt için sonuçlar

~ Ekim 2000 Bu sorun için geniş çapta uyarlanmış, değiştirilmiş, daha sonra iyi anlaşılmamış ve unutulmuş bir algoritma yayınladım. Bu sorunu çözmek için, 2008 yılının Ağustos ayında algoritmayı açıklayan ve genel amaçlı JavaScript optimizasyonlarının basit bir örneği olarak kullanan bir http://www.webreference.com/programming/javascript/jkm3/3.html bir makale yayınladım . Şimdiye kadar, Web Başvurusu iletişim bilgilerimi ve hatta bu makaledeki adımı temizledi. Ve bir kez daha, algoritma geniş çapta uyarlandı, değiştirildi, daha sonra iyi anlaşılmadı ve büyük ölçüde unutuldu.

Orijinal dize tekrarlama / çarpma Joseph Myers tarafından JavaScript algoritması. Ağustos 2008'de bu başvuru ile Web Reference tarafından yayımlanmıştır: http://www.webreference.com/programming/javascript/jkm3/3.html (Makale işlevi, yalnızca garip olanlar için JavaScript optimizasyonlarına örnek olarak kullanılmıştır. adı "stringFill3.")

/*
 * Usage: stringFill3("abc", 2) == "abcabc"
 */

function stringFill3(x, n) {
    var s = '';
    for (;;) {
        if (n & 1) s += x;
        n >>= 1;
        if (n) x += x;
        else break;
    }
    return s;
}

Bu makalenin yayınlanmasından sonraki iki ay içinde, aynı soru Stack Overflow'a gönderildi ve görünüşe göre bu problemin orijinal algoritması bir kez daha unutulduğunda radarımın altında uçtu. Bu Yığın Taşması sayfasında bulunan en iyi çözüm, çözümümün muhtemelen birkaç nesille ayrılmış değiştirilmiş bir sürümüdür. Ne yazık ki değişiklikler modifikasyonun bozulmasına neden oldu. Aslında, halkanın yapısını orijinalimden değiştirerek, modifiye edilmiş çözüm, üstel çoğaltma işleminin tamamen gereksiz bir ekstra adımını gerçekleştirir (böylece uygun cevapta kullanılan en büyük dizgiyi kendisiyle bir ekstra zaman birleştirir ve sonra atar).

Aşağıda, bu sorunun tüm yanıtları ve herkesin yararına olan bazı JavaScript optimizasyonları hakkında bir tartışma sunulmaktadır.

Teknik: Nesnelere veya nesne özelliklerine başvurmaktan kaçının

Bu tekniğin nasıl çalıştığını göstermek için, gereken uzunlukta dizeler oluşturan gerçek hayattaki bir JavaScript işlevi kullanıyoruz. Göreceğimiz gibi, daha fazla optimizasyon eklenebilir!

Burada kullanılan gibi bir işlev, metin sütunlarını hizalamak, para biçimlendirmek veya blok verilerini sınıra kadar doldurmak için dolgu oluşturmaktır. Metin oluşturma işlevi, metin üzerinde çalışan diğer işlevleri test etmek için değişken uzunluklu girdilere de izin verir. Bu işlev, JavaScript metin işleme modülünün önemli bileşenlerinden biridir.

Devam ederken, orijinal kodu dizeler oluşturmak için optimize edilmiş bir algoritmaya dönüştürürken en önemli optimizasyon tekniklerinden iki tanesini daha ele alacağız. Nihai sonuç, her yerde kullandığım endüstriyel güçte, yüksek performanslı bir işlevdir - JavaScript sipariş formlarında, veri biçimlendirmesinde ve e-posta / metin mesajı biçimlendirmesinde ve diğer birçok kullanımda öğe fiyatlarını ve toplamları hizalar.

Dizeler oluşturmak için orijinal kod stringFill1()

function stringFill1(x, n) { 
    var s = ''; 
    while (s.length < n) s += x; 
    return s; 
} 
/* Example of output: stringFill1('x', 3) == 'xxx' */ 

Sözdizimi açıktır. Gördüğünüz gibi, daha fazla optimizasyona geçmeden önce zaten yerel işlev değişkenlerini kullandık.

Koddaki bir nesne özelliğine s.length, performansına zarar veren masum bir başvuru olduğunu unutmayın. Daha da kötüsü, bu nesne özelliğinin kullanımı, okuyucunun JavaScript dize nesnelerinin özellikleri hakkında bildiklerini varsayarak programın basitliğini azaltır.

Bu object özelliğinin kullanılması, bilgisayar programının genelliğini yok eder. Program xbir uzunluk dizesi olması gerektiğini varsayar . Bu, stringFill1()işlevin uygulanmasını tekli karakterlerin tekrarı dışında herhangi bir şeye sınırlar . HTML varlığı gibi birden çok bayt içeriyorsa, tek karakterler bile kullanılamaz &nbsp;.

Bir nesne özelliğinin bu gereksiz kullanımından kaynaklanan en kötü sorun, işlevin boş bir giriş dizesinde test edilmesi durumunda sonsuz bir döngü oluşturmasıdır x. Genelliği kontrol etmek için, mümkün olan en küçük girdi miktarına bir program uygulayın. Kullanılabilir bellek miktarını aşması istendiğinde çöken bir programın mazereti vardır. Hiçbir şey üretmemesi istendiğinde bunun gibi bir program kabul edilemez. Bazen güzel kod zehirli koddur.

Basitlik, bilgisayar programlamanın belirsiz bir hedefi olabilir, ancak genellikle değildir. Bir program makul düzeyde genelliğe sahip olmadığında, "Program gittiği kadar iyidir." Demek geçerli değildir. Gördüğünüz gibi, string.lengthözelliği kullanmak bu programın genel bir ortamda çalışmasını engeller ve aslında yanlış program bir tarayıcıya veya sistem çökmesine neden olmaya hazırdır.

Bu JavaScript'in performansını artırmanın ve bu iki ciddi soruna dikkat etmenin bir yolu var mı?

Elbette. Sadece tamsayıları kullanın.

Dizeler oluşturmak için optimize edilmiş kod stringFill2()

function stringFill2(x, n) { 
    var s = ''; 
    while (n-- > 0) s += x; 
    return s; 
} 

Karşılaştırılacak zamanlama kodu stringFill1()vestringFill2()

function testFill(functionToBeTested, outputSize) { 
    var i = 0, t0 = new Date(); 
    do { 
        functionToBeTested('x', outputSize); 
        t = new Date() - t0; 
        i++; 
    } while (t < 2000); 
    return t/i/1000; 
} 
seconds1 = testFill(stringFill1, 100); 
seconds2 = testFill(stringFill2, 100); 

Şimdiye kadarki başarı stringFill2()

stringFill1()100 baytlık bir dizeyi doldurmak için 47.297 mikrosaniye (saniyenin milyonda biri) stringFill2()alır ve aynı şeyi yapmak için 27,68 mikrosaniye alır. Bu, bir nesne özelliğine başvurudan kaçınarak performansta neredeyse iki katına çıkar.

Teknik: Uzun dizelere kısa dizeler eklemekten kaçının

Önceki sonucumuz iyi görünüyordu - aslında çok iyi. Geliştirilmiş fonksiyon stringFill2(), ilk iki optimizasyonumuzun kullanılması nedeniyle çok daha hızlıdır. Size şimdi olduğundan çok daha hızlı olmanın iyileştirilebileceğini söylersem buna inanır mıydınız?

Evet, bu hedefe ulaşabiliriz. Şu anda uzun dizelere kısa dizeler eklemekten nasıl kaçındığımızı açıklamamız gerekiyor.

Kısa vadeli davranış orijinal fonksiyonumuzla karşılaştırıldığında oldukça iyi görünmektedir. Bilgisayar bilimcileri, bir fonksiyonun veya bilgisayar programı algoritmasının "asimptotik davranışını" analiz etmeyi sever, bu da uzun vadeli davranışını daha büyük girdilerle test ederek incelemek anlamına gelir. Bazen başka testler yapılmadan, bir bilgisayar programının geliştirilebileceğinin hiçbir zaman farkında olmaz. Ne olacağını görmek için 200 baytlık bir dize oluşturacağız.

İle ortaya çıkan sorun stringFill2()

Zamanlama fonksiyonumuzu kullanarak, 200 baytlık bir dize için sürenin 62.54 mikrosaniyeye, 100 baytlık bir dize için 27.68'e yükseldiğini görüyoruz. Öyle görünüyor ki, iki kat daha fazla iş yapmak için zaman iki katına çıkmalı, ancak bunun yerine üç kat veya dört katına çıktı. Programlama deneyiminden, bu sonuç garip görünüyor, çünkü herhangi bir şey varsa, iş daha verimli bir şekilde yapıldığından işlev biraz daha hızlı olmalıdır (işlev çağrısı başına 100 bayt yerine işlev çağrısı başına 200 bayt). Bu sorun, JavaScript dizelerinin sinsi bir özelliği ile ilgilidir: JavaScript dizeleri "değişmez" dir.

Değişmez, bir dizeyi oluşturulduktan sonra değiştiremeyeceğiniz anlamına gelir. Her seferinde bir bayt ekleyerek, bir bayt daha fazla çaba harcamayız. Aslında tüm dizeyi artı bir bayt daha oluşturuyoruz.

Aslında, 100 baytlık bir dizeye bir bayt daha eklemek için 101 bayt değerinde iş gerekir. Bir Nbayt dizisi oluşturmak için hesaplama maliyetini kısaca analiz edelim . İlk baytı eklemenin maliyeti 1 birim hesaplama çabasıdır. İkinci bayt eklemenin maliyeti bir birim değil 2 birimdir (ilk baytı yeni bir dize nesnesine kopyalamak ve ikinci baytı eklemek). Üçüncü bayt için 3 birim vb.

C(N) = 1 + 2 + 3 + ... + N = N(N+1)/2 = O(N^2). Sembol O(N^2), N kare büyük O olarak telaffuz edilir ve uzun vadede hesaplama maliyetinin dize uzunluğunun karesiyle orantılı olduğu anlamına gelir. 100 karakter oluşturmak için 10.000 adet iş gerekir ve 200 karakter oluşturmak için 40.000 adet iş gerekir.

Bu yüzden 100 karakterden 200 karakter oluşturmak iki kattan daha uzun sürdü. Aslında, dört kat daha uzun sürmüş olmalıydı. Programlama deneyimimiz, işin daha uzun dizeler için biraz daha verimli bir şekilde yapılması ve dolayısıyla sadece üç kat daha uzun sürmesi açısından doğruydu. İşlev çağrısının ek yükü, oluşturduğumuz bir dizenin uzunluğu konusunda önemsiz hale geldiğinde, aslında bir dizenin iki katı uzunluğunda bir dizenin oluşturulması dört kat daha fazla zaman alır.

(Tarihsel not: Bu analiz, kaynak koddaki dizeler için geçerli değildir html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n', çünkü JavaScript kaynak kodu derleyicisi bir JavaScript dizesi nesnesine dönüştürülmeden önce dizeleri birleştirebilir. Sadece birkaç yıl önce KJS uygulaması Artı işaretleriyle birleştirilen uzun kaynak kodu dizeleri yüklenirken JavaScript donuyor veya çöküyor Hesaplama süresinden bu yana O(N^2), KJS JavaScript motor çekirdeğini kullanan Konqueror Web tarayıcısını veya Safari'yi aşırı yükleyen Web sayfaları yapmak zor değildi. bir biçimlendirme dili ve JavaScript biçimlendirme dili ayrıştırıcısı geliştirirken bu sorunla karşılaştım ve ardından JavaScript İçerdiği komut dosyasını yazdığımda soruna neyin neden olduğunu keşfettim.)

Açıkçası, bu hızlı performans bozulması büyük bir sorundur. JavaScript'in dizeleri değiştirilemez nesneler olarak işleme biçimini değiştiremediğimiz için bununla nasıl başa çıkabiliriz? Çözüm, dizeyi mümkün olduğunca az sayıda yeniden oluşturan bir algoritma kullanmaktır.

Açıklığa kavuşturmak için hedefimiz, uzun dizelere kısa dizeler eklemekten kaçınmaktır, çünkü kısa dizeyi eklemek için tüm uzun dizenin de çoğaltılması gerekir.

Uzun dizelere kısa dizeler eklemekten kaçınmak için algoritma nasıl çalışır?

Yeni dize nesnelerinin oluşturulma sayısını azaltmanın iyi bir yolu. Çıktıya bir seferde birden fazla bayt eklenecek şekilde daha uzun dize uzunluklarını birleştirin.

Örneğin, bir uzunluk dizesi yapmak için N = 9:

x = 'x'; 
s = ''; 
s += x; /* Now s = 'x' */ 
x += x; /* Now x = 'xx' */ 
x += x; /* Now x = 'xxxx' */ 
x += x; /* Now x = 'xxxxxxxx' */ 
s += x; /* Now s = 'xxxxxxxxx' as desired */

Bunu yapmak için 1 uzunluklu bir dize oluşturmak, 2 uzunluklu bir dize oluşturmak, 4 uzunluklu bir dize oluşturmak, 8 uzunluklu bir dize oluşturmak ve son olarak 9 uzunluklu bir dize oluşturmak gerekir. Ne kadar maliyet tasarrufu sağladık?

Eski maliyet C(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 9 = 45.

Yeni maliyet C(9) = 1 + 2 + 4 + 8 + 9 = 24.

0 uzunluğunda bir dizeye 1 uzunluğunda bir dize, 1 uzunluğunda bir dize 1 uzunluğunda bir dize, 2 uzunluğunda bir dizeye 2 uzunluğunda bir dize, ardından 4 uzunluğunda bir dize eklememiz gerektiğine dikkat edin. bir uzunluk dizesi 4'e, daha sonra bir uzunluk dizesi elde etmek için 8 uzunluk dizesine 1 uzunluk dizesine 9, yaptığımız şey uzun dizelere kısa dizeler eklemekten kaçınmak veya eşit, neredeyse eşit uzunlukta dizeleri birleştirmeye çalışıyor.

Eski hesaplama maliyeti için bir formül bulduk N(N+1)/2. Yeni maliyet için bir formül var mı? Evet, ama karmaşık. Önemli olan şey, bu O(N)nedenle dize uzunluğunu iki katına çıkarmak, dört katına çıkarmak yerine iş miktarını yaklaşık iki katına çıkaracaktır.

Bu yeni fikri uygulayan kod, hesaplama maliyetinin formülü kadar karmaşıktır. Okuduğunuzda, bunun >>= 11 bayt sağa kaydırılması anlamına geldiğini unutmayın . Yani n = 10011bir ikili sayı n >>= 1ise, değerle sonuçlanır n = 1001.

Kodun tanıyamayacağınız diğer kısmı, bitsel ve operatördür, yazılmıştır &. İfadesinin n & 1son ikili basamağı n1 ise true, son ikili basamağı n0 ise false olarak değerlendirilir .

Yeni yüksek verimli stringFill3()işlev

function stringFill3(x, n) { 
    var s = ''; 
    for (;;) { 
        if (n & 1) s += x; 
        n >>= 1; 
        if (n) x += x; 
        else break; 
    } 
    return s; 
} 

Eğitimsiz göze çirkin görünüyor, ancak performansı hoş olmaktan daha az bir şey değil.

Bu işlevin ne kadar iyi performans gösterdiğini görelim. Sonuçları gördükten sonra, bir O(N^2)algoritma ile bir O(N)algoritma arasındaki farkı asla unutmayacaksınız .

stringFill1()200 baytlık bir dize oluşturmak için 88.7 mikrosaniye (saniyenin milyonda biri) stringFill2()alır, 62.54 stringFill3()alır ve yalnızca 4.608 alır. Bu algoritmayı daha iyi yapan nedir? Tüm işlevler yerel işlev değişkenlerini kullanmaktan faydalandı, ancak ikinci ve üçüncü optimizasyon tekniklerinden yararlanmak performansına yirmi kat iyileşme sağladı stringFill3().

Daha derin analiz

Bu özel işlevi rekabeti suyun dışına iten şey nedir?

Daha önce de bahsettiğim gibi, bu işlevlerin her ikisi nedeni, stringFill1()ve stringFill2()bu yüzden yavaş çalışmasına JavaScript dizeleri değişmez olmasıdır. JavaScript tarafından saklanan dize verilerine bir seferde bir bayt daha eklenmesine izin vermek için bellek yeniden tahsis edilemez. Dizenin sonuna bir bayt daha eklendiğinde, dizenin tamamı baştan sona yeniden oluşturulur.

Bu nedenle, komut dosyasının performansını iyileştirmek için, önceden iki dizeyi birleştirerek ve sonra istenen dize uzunluğunu yinelemeli olarak oluşturarak daha uzun uzunluklu dizeleri önceden hesaplamak gerekir.

Örneğin, 16 harfli bir bayt dizesi oluşturmak için, önce iki baytlık bir dize önceden hesaplanır. Daha sonra dört baytlık bir dizeyi önceden hesaplamak için iki baytlık dize yeniden kullanılır. Ardından, sekiz baytlık bir dizeyi önceden hesaplamak için dört baytlık dize yeniden kullanılır. Son olarak, sekiz baytlık iki dizgi, 16 baytlık istenen yeni dizeyi oluşturmak için yeniden kullanılabilir. Toplamda 2 yeni, biri 4 uzunluğunda, biri 8 uzunluğunda ve 16 uzunluğunda olmak üzere dört yeni dizenin oluşturulması gerekiyordu. Toplam maliyet 2 + 4 + 8 + 16 = 30'dur.

Uzun vadede bu verimlilik, ters sırayla eklenerek ve ilk terim a1 = N ile başlayan ve ortak r = 1/2 oranına sahip geometrik bir seri kullanılarak hesaplanabilir. Geometrik bir serinin toplamı tarafından verilir a_1 / (1-r) = 2N.

Bu, 16'ya kadar yeni bir uzunluk 2 dizesi oluşturmak, 3, 4, 5 vb. Uzunluğunda yeni bir dize oluşturmak için bir karakter eklemekten daha etkilidir. Önceki algoritma, bir seferde tek bir bayt ekleme işlemini kullandı. ve toplam maliyeti olurdu n (n + 1) / 2 = 16 (17) / 2 = 8 (17) = 136.

Açıkçası, 136 30'dan çok daha büyük bir sayıdır ve bu nedenle önceki algoritma bir dize oluşturmak için çok, çok daha fazla zaman alır.

İki yöntemi karşılaştırmak için, özyinelemeli algoritmanın ("böl ve fethet" de denir) 123.457 uzunluğunda bir dizede ne kadar hızlı olduğunu görebilirsiniz. FreeBSD bilgisayarımda stringFill3()işlevde uygulanan bu algoritma dizeyi 0.001058 saniyede stringFill1()oluştururken, orijinal işlev dizeyi 0.0808 saniyede oluşturur. Yeni işlev 76 kat daha hızlı.

Dizginin uzunluğu büyüdükçe performans farkı artar. Daha büyük ve daha büyük dizeler oluşturuldukça sınırda, orijinal işlev kabaca C1(sabit) süreler N^2gibi davranır ve yeni işlev C2(sabit) süreler gibi davranır N.

Deneyimizden, C1olmanın C1 = 0.0808 / (123457)2 = .00000000000530126997değerini ve C2olmanın değerini belirleyebiliriz C2 = 0.001058 / 123457 = .00000000856978543136. 10 saniye içinde, yeni işlev 1.166.890.359 karakter içeren bir dize oluşturabilir. Aynı dizeyi oluşturmak için eski işlevin 7.218.384 saniyeye ihtiyacı vardır.

Bu on saniyeye kıyasla neredeyse üç ay!

Sadece (birkaç yıl geç) cevap veriyorum çünkü bu soruna orijinal çözümüm 10 yıldan fazla bir süredir internette yüzüyor ve görünüşe göre hala hatırlayan az sayıda kişi tarafından anlaşılamıyor. Bu konuda bir makale yazarak yardımcı olacağımı düşündüm:

Yüksek Hızlı JavaScript için Performans Optimizasyonu / Sayfa 3

Ne yazık ki, burada sunulan diğer çözümlerden bazıları, uygun bir çözümün 10 saniye içinde yarattığı aynı miktarda çıktı üretmek için üç ay süren çözümlerden bazılarıdır.

Yığın taşması üzerine kanonik bir cevap olarak makalenin bir bölümünü yeniden oluşturmak için zaman ayırmak istiyorum.

Burada en iyi performans gösteren algoritmanın algoritmamı temel aldığını ve muhtemelen bir başkasının 3. veya 4. nesil adaptasyonundan miras alındığını unutmayın. Ne yazık ki, değişiklikler performansının düşmesine neden oldu. Burada sunulan çözümümün varyasyonu, belki de for (;;)C ile yazılmış bir sunucunun ana sonsuz döngüsüne benzeyen ve basitçe döngü kontrolü için dikkatle konumlandırılmış bir kesme ifadesine izin vermek için tasarlanmış kafa karıştırıcı ifademi anlayamadı . dizeyi katlanarak gereksiz yere bir kez çoğaltmaktan kaçının.


4
Bu cevap o kadar çok oy almamış olmalı. Her şeyden önce, Joseph'in sahiplik iddiaları alaycıdır. Temel algoritma 3700 yaşındadır.
artistoex

2
İkincisi, çok fazla yanlış bilgi içeriyor. Modern Javascript uygulamaları, birleştirme yaparken bir dizenin içeriğine bile dokunmaz (v8, ConsString türünde bir nesne olarak birleştirilmiş dizeleri temsil eder). Geri kalan tüm geliştirmeler ihmal edilebilir (asimtotik karmaşıklık açısından).
artistoex

3
Dizelerin nasıl birleştirildiği hakkındaki fikriniz yanlış. İki dizeyi birleştirmek için Javascript, kurucu dizelerin baytlarını hiç okumaz. Bunun yerine, yalnızca sol ve sağ kısımları ifade eden bir nesne oluşturur. Bu yüzden döngüdeki son birleştirme, birinciden daha pahalı değildir.
artistoex

3
Tabii ki, bu, ipin indekslenmesi için O (1) maliyetinden daha yüksek bir maliyetle sonuçlanır, bu nedenle birleştirme, daha fazla değerlendirmeyi hak eden lateronu düzleştirebilir.
artistoex

1
Bu mükemmel bir okuma oldu. Verimlilik hakkında bir kitap yazmalısınız!

39

Bu oldukça verimli

String.prototype.repeat = function(times){
    var result="";
    var pattern=this;
    while (times > 0) {
        if (times&1)
            result+=pattern;
        times>>=1;
        pattern+=pattern;
    }
    return result;
};

11
@Olegs, oylama fikrinin bir kişiye veya bir kişinin yaratıcılığına (aslında alkışlanabilir) oy vermekten daha az olduğunu düşünüyorum, ancak fikir en eksiksiz çözüm için oy vermek, böylece kolayca bulunabilir. listenin en tepesinde, mükemmel olanı bulmak için tüm cevapları okumak zorunda kalmadan. (Çünkü maalesef, hepimizin sınırlı bir zamanı var ...)
Sorin Postelnicu

38

İyi haberler! String.prototype.repeatolduğu şimdi JavaScript bir parçası .

"yo".repeat(2);
// returns: "yoyo"

Yöntem, Internet Explorer ve Android Webview hariç tüm büyük tarayıcılar tarafından desteklenmektedir. Güncel bir liste için bkz. MDN: String.prototype.repeat> Tarayıcı uyumluluğu .

MDN, desteksiz tarayıcılar için bir çoklu dolguya sahiptir .


Mevcut durum hakkında rapor verdiğiniz için teşekkürler, ancak Mozilla çoklu dolgusunun çoğu ihtiyaç için çok karmaşık olduğunu düşünüyorum (verimli C uygulamalarının davranışlarını taklit etmeye çalıştıklarını varsayıyorum) - bu yüzden OP'nin kararlılık gereksinimini gerçekten cevaplamayacaksınız. Çoklu dolgu olarak kurulan diğer yaklaşımlardan herhangi biri daha özlü olmak zorundadır ;-)
Guss

2
Kesinlikle! Ancak yerleşik kullanımı en özlü sürüm olmalıdır. Çoklu dolgular temel olarak sadece arka bağlantı noktaları olduğundan, teknik özelliklerle (veya bu durumda önerilen teknik özelliklerle) uyumluluğu sağlamak için biraz karmaşık olma eğilimindedirler. Tamlık için ekledim, hangi yöntemi kullanacağına karar vermek sanırım.
André Laszlo


17

P.Bailey'in çözümünü genişletmek :

String.prototype.repeat = function(num) {
    return new Array(isNaN(num)? 1 : ++num).join(this);
    }

Bu şekilde beklenmedik argüman türlerinden güvende olmalısınız:

var foo = 'bar';
alert(foo.repeat(3));              // Will work, "barbarbar"
alert(foo.repeat('3'));            // Same as above
alert(foo.repeat(true));           // Same as foo.repeat(1)

alert(foo.repeat(0));              // This and all the following return an empty
alert(foo.repeat(false));          // string while not causing an exception
alert(foo.repeat(null));
alert(foo.repeat(undefined));
alert(foo.repeat({}));             // Object
alert(foo.repeat(function () {})); // Function

EDIT: Zarif fikri için jerone için kredi ++num!


2
Senin biraz değişti:String.prototype.repeat = function(n){return new Array(isNaN(n) ? 1 : ++n).join(this);}
jerone

Her neyse, bu teste göre ( jsperf.com/string-repeat/2 ) string konkatantı ile basit bir döngü yapmak, Array.join kullanmaya kıyasla Chrome'da çok daha hızlı görünüyor. Komik değil mi ?!
Marco Demaio


5
/**  
@desc: repeat string  
@param: n - times  
@param: d - delimiter  
*/

String.prototype.repeat = function (n, d) {
    return --n ? this + (d || '') + this.repeat(n, d) : '' + this
};

bu, sınırlayıcıyı kullanarak dizeyi birkaç kez tekrarlamaktır.


4

İşte disfated'ın cevabında% 5-7'lik bir iyileşme.

Atlayarak döngüyü açın ve döngüden sonra count > 1ek bir result += pattnernconcat gerçekleştirin . Bu, pattern += patternpahalı bir if-check kullanmak zorunda kalmadan daha önce kullanılmayan döngülerin sonunu önleyecektir . Nihai sonuç şöyle görünecektir:

String.prototype.repeat = function(count) {
    if (count < 1) return '';
    var result = '', pattern = this.valueOf();
    while (count > 1) {
        if (count & 1) result += pattern;
        count >>= 1, pattern += pattern;
    }
    result += pattern;
    return result;
};

Ve işte disfated'ın keman, unrolled versiyonu için çatallandı: http://jsfiddle.net/wsdfg/


2
function repeat(s, n) { var r=""; for (var a=0;a<n;a++) r+=s; return r;}

2
Dize birleştirmesi maliyetli değil mi? En azından Java'da durum böyle.
Vijay Dev

Neden evet. Ancak, javarscript'te gerçekten optimize edilemez. :(
McTrafik

Bu performans geliştirmesine ne dersiniz: var r=s; for (var a=1;...:)))) Her neyse, bu teste göre ( jsperf.com/string-repeat/2 ), dizi üzerinde önerdiğiniz gibi dize konkalasyonu olan bir döngü için basit bir işlem yapmak, Array'ı kullanmaya kıyasla Chrome'da çok daha hızlı görünüyor .katılmak.
Marco Demaio

@VijayDev - bu teste göre değil: jsperf.com/ultimate-concat-vs-join
jbyrd

2

Çeşitli yöntemlerin testleri:

var repeatMethods = {
    control: function (n,s) {
        /* all of these lines are common to all methods */
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        return '';
    },
    divideAndConquer:   function (n, s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        with(Math) { return arguments.callee(floor(n/2), s)+arguments.callee(ceil(n/2), s); }
    },
    linearRecurse: function (n,s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        return s+arguments.callee(--n, s);
    },
    newArray: function (n, s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        return (new Array(isNaN(n) ? 1 : ++n)).join(s);
    },
    fillAndJoin: function (n, s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        var ret = [];
        for (var i=0; i<n; i++)
            ret.push(s);
        return ret.join('');
    },
    concat: function (n,s) {
        if (n==0) return '';
        if (n==1 || isNaN(n)) return s;
        var ret = '';
        for (var i=0; i<n; i++)
            ret+=s;
        return ret;
    },
    artistoex: function (n,s) {
        var result = '';
        while (n>0) {
            if (n&1) result+=s;
            n>>=1, s+=s;
        };
        return result;
    }
};
function testNum(len, dev) {
    with(Math) { return round(len+1+dev*(random()-0.5)); }
}
function testString(len, dev) {
    return (new Array(testNum(len, dev))).join(' ');
}
var testTime = 1000,
    tests = {
        biggie: { str: { len: 25, dev: 12 }, rep: {len: 200, dev: 50 } },
        smalls: { str: { len: 5, dev: 5}, rep: { len: 5, dev: 5 } }
    };
var testCount = 0;
var winnar = null;
var inflight = 0;
for (var methodName in repeatMethods) {
    var method = repeatMethods[methodName];
    for (var testName in tests) {
        testCount++;
        var test = tests[testName];
        var testId = methodName+':'+testName;
        var result = {
            id: testId,
            testParams: test
        }
        result.count=0;

        (function (result) {
            inflight++;
            setTimeout(function () {
                result.start = +new Date();
                while ((new Date() - result.start) < testTime) {
                    method(testNum(test.rep.len, test.rep.dev), testString(test.str.len, test.str.dev));
                    result.count++;
                }
                result.end = +new Date();
                result.rate = 1000*result.count/(result.end-result.start)
                console.log(result);
                if (winnar === null || winnar.rate < result.rate) winnar = result;
                inflight--;
                if (inflight==0) {
                    console.log('The winner: ');
                    console.log(winnar);
                }
            }, (100+testTime)*testCount);
        }(result));
    }
}

2

İşte JSLint güvenli sürümü

String.prototype.repeat = function (num) {
  var a = [];
  a.length = num << 0 + 1;
  return a.join(this);
};

2

Tüm tarayıcılar için

Bu mümkün olduğu kadar özlü:

function repeat(s, n) { return new Array(n+1).join(s); }

Performansı da önemsiyorsanız, bu çok daha iyi bir yaklaşımdır:

function repeat(s, n) { var a=[],i=0;for(;i<n;)a[i++]=s;return a.join(''); }

Her iki seçeneğin performansını karşılaştırmak istiyorsanız, bu Fiddle ve bu Fiddle'a bakın testleri için bakın. Kendi testlerim sırasında, ikinci seçenek Firefox'ta yaklaşık 2 kat ve Chrome'da yaklaşık 4 kat daha hızlıydı!

Yalnızca modern tarayıcılar için:

Modern tarayıcılarda artık şunları da yapabilirsiniz:

function repeat(s,n) { return s.repeat(n) };

Bu seçenek sadece diğer seçeneklerden daha kısa değil, aynı zamanda daha da hızlı zamanda ikinci seçenekten .

Ne yazık ki, Internet explorer'ın herhangi bir sürümünde çalışmıyor. Tablodaki sayılar, yöntemi tam olarak destekleyen ilk tarayıcı sürümünü belirtir:

resim açıklamasını buraya girin



2

Başka bir tekrarlama işlevi:

function repeat(s, n) {
  var str = '';
  for (var i = 0; i < n; i++) {
    str += s;
  }
  return str;
}

2

ES2015 bunu gerçekleştirdi repeat() yöntem !

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/ Dize / yineleme
http://www.w3schools.com/jsref/jsref_repeat.asp

/** 
 * str: String
 * count: Number
 */
const str = `hello repeat!\n`, count = 3;

let resultString = str.repeat(count);

console.log(`resultString = \n${resultString}`);
/*
resultString = 
hello repeat!
hello repeat!
hello repeat!
*/

({ toString: () => 'abc', repeat: String.prototype.repeat }).repeat(2);
// 'abcabc' (repeat() is a generic method)

// Examples

'abc'.repeat(0);    // ''
'abc'.repeat(1);    // 'abc'
'abc'.repeat(2);    // 'abcabc'
'abc'.repeat(3.5);  // 'abcabcabc' (count will be converted to integer)
// 'abc'.repeat(1/0);  // RangeError
// 'abc'.repeat(-1);   // RangeError


1

Bu en küçük özyinelemeli olabilir: -

String.prototype.repeat = function(n,s) {
s = s || ""
if(n>0) {
   s += this
   s = this.repeat(--n,s)
}
return s}


1

Basit özyinelemeli birleştirme

Sadece bir bash vermek istedim ve bunu yaptım:

function ditto( s, r, c ) {
    return c-- ? ditto( s, r += s, c ) : r;
}

ditto( "foo", "", 128 );

Ben çok düşündüm diyemeyiz ve muhtemelen :-) gösterir

Bu tartışmasız daha iyi

String.prototype.ditto = function( c ) {
    return --c ? this + this.ditto( c ) : this;
};

"foo".ditto( 128 );

Ve daha önce gönderilmiş bir cevap gibi - bunu biliyorum.

Peki neden özyinelemeli?

Ve biraz varsayılan davranışa ne dersiniz?

String.prototype.ditto = function() {
    var c = Number( arguments[ 0 ] ) || 2,
        r = this.valueOf();
    while ( --c ) {
        r += this;
    }
    return r;
}

"foo".ditto();

Çünkü , özyinelemesiz yöntem, çağrı yığını sınırlarına çarpmadan keyfi olarak büyük tekrarları işleyecek olsa da, çok daha yavaştır.

Neden yarısı kadar akıllı olmayan daha fazla yöntem eklemeye zahmet ettim? zaten yayınlanmış olanların ?

Kısmen kendi eğlencem için ve kısmen en basit şekilde işaret etmek için bir kediyi ciltlemenin birçok yolu olduğunu biliyorum ve duruma bağlı olarak, görünüşe göre en iyi yöntemin ideal olmaması oldukça mümkündür .

Nispeten hızlı ve sofistike bir yöntem belirli koşullar altında etkili bir şekilde çökebilir ve yanabilirken, daha yavaş, daha basit bir yöntem işi sonunda yapabilir.

Bazı yöntemler patlatır biraz daha olabilir ve eğilimli gibi olan sabit varlık dışarı ve diğer yöntemler her koşulda güzel çalışabilir, ancak böylece inşa edilir bir basitçe nasıl çalıştığını bilmiyor.

“Peki ya nasıl çalıştığını bilmezsem ?!”

Ciddi anlamda?

JavaScript en güçlü yönlerinden birini yaşar; kötü davranışlara karşı son derece toleranslıdır ve o kadar esnektir ki, sonuç elde etmek için tersine doğru eğilir, eğer koparsa herkes için daha iyi olabilirdi!

"Büyük güç büyük sorumluluk getirir" ;-)

Ama daha ciddi ve daha önemlisi, bu gibi genel sorular şeklinde muhteşemliğinin yol rağmen akıllı cevaplar başka bir şey, kişinin bilgi ve ufuklarını genişletmek eğer sonunda, el altında görev - pratik komut that use çıkan yöntemi - biraz daha az ya da biraz daha zeki olabilir önerilenden .

Bu "mükemmel" algoritmalar eğlenceli ve hepsidir, ancak "tek beden herkese uyar" , özel olarak yapılandan daha iyi olursa nadiren olacaktır.

Bu vaaz, uyku eksikliği ve geçici bir ilgi nedeniyle size getirildi. Devam edin ve kodlayın!


1

Birincisi, OP'nin soruları, "basit ve okunması kolay" anlamına geldiğini anladığım kısa ve verimlilikle ilgili gibi görünüyor - ki bu kesinlikle aynı şey değildir ve aynı zamanda belirli büyük veri işleme algoritmaları, temel veri işleme Javascript işlevlerini uygulamaya geldiğinizde sizi endişelendirmemelidir. Özlülük çok daha önemlidir.

İkinci olarak, André Laszlo'nun belirttiği gibi, String.repeat, ECMAScript 6'nın bir parçasıdır ve birçok popüler uygulamada zaten mevcuttur - bu yüzden en özlü String.repeatuygulaması onu uygulamak değildir ;-)

Son olarak, ECMAScript 6 uygulamasını sunmayan ana bilgisayarları desteklemeniz gerekiyorsa, MDN'nin André Laszlo tarafından bahsedilen çoklu dolgusu kısa ve öz bir şeydir.

Yani, daha fazla uzatmadan - İşte benim özlü poli dolum:

String.prototype.repeat = String.prototype.repeat || function(n){
    return n<=1 ? this : this.concat(this.repeat(n-1));
}

Evet, bu bir özyineleme. Özyinelemeleri severim - basittirler ve doğru yapılırsa anlaşılması kolaydır. Verimlilik ile ilgili olarak, dil destekliyorsa, doğru yazılırsa çok verimli olabilirler.

Testlerimden, bu yöntem Array.joinyaklaşımdan ~% 60 daha hızlı . Açıkça anlaşılan disfated'in hiçbir yerinde olmadığı halde, her ikisinden de çok daha basittir.

Benim test kurulum "Sıkı mod" (Bence bir tür TCO sağlar ) kullanarak düğüm v0.10, repeat(1000)bir milyon kez 10 karakter dizesi çağırıyor .


1

Tüm bu prototip tanımlarının, dizi oluşturmalarının ve birleştirme işlemlerinin aşırı olduğunu düşünüyorsanız, ihtiyacınız olan yerde tek bir satır kodu kullanın. N kez yinelenen String S:

for (var i = 0, result = ''; i < N; i++) result += S;

3
Kod okunabilir olmalıdır. Kelimenin tam anlamıyla yalnızca bir kez kullanacaksanız, düzgün bir şekilde biçimlendirin (veya Array(N + 1).join(str)bir performans darboğazı değilse yöntemi kullanın ). İki kez kullanma şansınız en düşükse, uygun şekilde adlandırılmış bir işleve taşıyın.
cloudfeet

1

Dizeleri yineleme gibi Javascript yardımcı programı işlevleri için Lodash'ı kullanın.

Lodash güzel performans ve ECMAScript uyumluluğu sağlar.

UI geliştirme için kesinlikle tavsiye ederim ve çok iyi sunucu tarafında çalışır.

Lodash kullanarak "yo" dizesini 2 kez tekrarlamak için:

> _.repeat('yo', 2)
"yoyo"

0

Bölme ve fethetme kullanarak özyinelemeli çözüm:

function repeat(n, s) {
    if (n==0) return '';
    if (n==1 || isNaN(n)) return s;
    with(Math) { return repeat(floor(n/2), s)+repeat(ceil(n/2), s); }
}

0

Buraya rastgele geldim ve daha önce javascript'te bir karakter tekrarlamak için bir nedenim yoktu.

Artistoex'in bunu yapma şeklinden ve disfated sonuçlarından etkilendim. Dennis'in de işaret ettiği gibi, son dize konsatının gereksiz olduğunu fark ettim.

Disfated örnekleme ile birlikte oynarken birkaç şey daha fark ettim.

Sonuçlar genellikle son çalışmayı destekleyen adil bir miktar değişiyordu ve benzer algoritmalar genellikle pozisyon için jokey yapıyordu. Değiştirdiğim şeylerden biri, çağrılar için tohum olarak JSLitmus üretilen sayısını kullanmaktı; sayısı çeşitli yöntemler için farklı üretildiğinden, bir indeks koydum. Bu, işi çok daha güvenilir hale getirdi. Sonra değişen büyüklükte dizelerin fonksiyonlara aktarılmasını sağladım. Bu, bazı algoritmaların tek karakterlerde veya daha küçük dizelerde daha iyi performans gösterdiği bazı varyasyonları engelledi. Bununla birlikte, dize büyüklüğünden bağımsız olarak ilk 3 yöntem iyi sonuç verdi.

Çatallı test seti

http://jsfiddle.net/schmide/fCqp3/134/

// repeated string
var string = '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789';
// count paremeter is changed on every test iteration, limit it's maximum value here
var maxCount = 200;

var n = 0;
$.each(tests, function (name) {
    var fn = tests[name];
    JSLitmus.test(++n + '. ' + name, function (count) {
        var index = 0;
        while (count--) {
            fn.call(string.slice(0, index % string.length), index % maxCount);
            index++;
        }
    });
    if (fn.call('>', 10).length !== 10) $('body').prepend('<h1>Error in "' + name + '"</h1>');
});

JSLitmus.runAll();

Daha sonra Dennis'in düzeltmesini ekledim ve biraz daha fazla dışarı çıkmak için bir yol bulabileceğimi görmeye karar verdim.

Javascript işleri gerçekten optimize edemediğinden, performansı artırmanın en iyi yolu şeylerden manuel olarak kaçınmaktır. İlk 4 önemsiz sonucu döngüden çıkarırsam, 2-4 string deposundan kaçınabilir ve nihai mağazayı doğrudan sonuca yazabilirim.

// final: growing pattern + prototypejs check (count < 1)
'final avoid': function (count) {
    if (!count) return '';
    if (count == 1) return this.valueOf();
    var pattern = this.valueOf();
    if (count == 2) return pattern + pattern;
    if (count == 3) return pattern + pattern + pattern;
    var result;
    if (count & 1) result = pattern;
    else result = '';
    count >>= 1;
    do {
        pattern += pattern;
        if (count & 1) result += pattern;
        count >>= 1;
    } while (count > 1);
    return result + pattern + pattern;
}

Bu Dennis'in fikrine göre ortalama% 1-2 iyileşme ile sonuçlandı. Ancak, farklı çalıştırmalar ve farklı tarayıcılar, bu ekstra kodun muhtemelen önceki 2 algoritma üzerinde çaba harcamaya değmeyecek kadar adil bir fark gösterecektir.

Bir tablo

Düzenleme: Bunu çoğunlukla krom altında yaptım. Firefox ve IE genellikle Dennis'i% 10 oranında tercih eder.


0

Basit yöntem:

String.prototype.repeat = function(num) {
    num = parseInt(num);
    if (num < 0) return '';
    return new Array(num + 1).join(this);
}

0

İnsanlar bunu gülünç derecede boşa harcıyor veya performansı boşa harcıyor. Diziler? Özyineleme? Şaka yapıyor olmalısın.

function repeat (string, times) {
  var result = ''
  while (times-- > 0) result += string
  return result
}

Düzenle. Ben artistoex / disfated ve bir sürü başka insan tarafından yayınlanan bitwise sürümü ile karşılaştırmak için bazı basit testler yaptım. İkincisi sadece marjinal olarak daha hızlıydı, ancak büyüklük sıraları bellekte daha verimli. 'Blah' kelimesinin 1000000 tekrarı için, Düğüm işlemi basit birleştirme algoritmasıyla (yukarıda) 46 megabayta kadar yükseldi, ancak logaritmik algoritmayla sadece 5.5 megabayta çıktı. İkincisi kesinlikle gitmek için bir yoldur. Anlaşılır olması için tekrar yayınlamak:

function repeat (string, times) {
  var result = ''
  while (times > 0) {
    if (times & 1) result += string
    times >>= 1
    string += string
  }
  return result
}

Fazla string += stringzamanın var.
Nikolay

0

Dizeleri bir sayıya göre birleştirme.

function concatStr(str, num) {
   var arr = [];

   //Construct an array
   for (var i = 0; i < num; i++)
      arr[i] = str;

   //Join all elements
   str = arr.join('');

   return str;
}

console.log(concatStr("abc", 3));

Umarım yardımcı olur!


0

ES8 ile bunun için padStartveya kullanabilirsiniz padEnd. Örneğin.

var str = 'cat';
var num = 23;
var size = str.length * num;
"".padStart(size, str) // outputs: 'catcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcat'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.