重复字符串-Javascript

2020/10/08 03:21 · javascript ·  · 0评论

返回任意次数重复的字符串的最佳或最简洁的方法是什么?

以下是到目前为止我最好的照片:

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

给新读者的注意:这个答案是旧的并且不是很实用-它只是“聪明”,因为它使用Array的东西来完成String的事情。当我写“更少的过程”时,我绝对是指“更少的代码”,因为正如其他人在随后的回答中所指出的那样,它的表现像猪一样。因此,如果速度对您很重要,请不要使用它。

我直接将此函数放到String对象上。无需创建数组,填充数组并将其与空字符连接,只需创建适当长度的数组,然后将其与所需的字符串连接即可。结果相同,过程更少!

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

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

我已经测试了所有建议方法的性能。

这是我得到的最快的变体

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;
};

或作为独立功能:

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算法。真的很快。并且count,与传统new Array(count + 1).join(string)方法相比,它越大,运行速度越快

我只更改了两件事:

  1. 替换pattern = thispattern = this.valueOf()(清除了一种明显的类型转换);
  2. if (count < 1)原型顶部检查添加到函数顶部,以排除在这种情况下不必要的操作。
  3. 丹尼斯 答案的应用优化(加快了5-7%)

UPD

在这里为感兴趣的人创建了一个性能测试的小操场

变量count〜0 .. 100:

性能图

常数count= 1024:

性能图

如果可以的话,请使用它并使其更快:)

此问题是JavaScript的一个众所周知的“经典”优化问题,原因是JavaScript字符串是“不可变的”,而且即使将单个字符串联在一起也需要创建,包括创建内存分配和复制到,一个新的字符串。

不幸的是,此页面上的可接受答案是错误的,其中“错误”的意思是简单的一个字符字符串的性能系数是3倍,短字符串重复多次的性能系数是8x-97x,重复句子的性能系数是300x,而当错误系数无限大将算法复杂度的比率限制n为无穷大。另外,此页面上还有另一个答案几乎是正确的(基于过去13年中在整个Internet上传播的正确解决方案的许多代和变体之一)。但是,这种“几乎正确”的解决方案错过了正确算法的关键点,从而导致性能下降50%。

JS性能结果:可接受的答案,效果最佳的其他答案(基于此答案中原始算法的降级版本),以及使用我13年前创建的算法得出的答案

〜2000年10月,我发布了针对该确切问题的算法,对该算法进行了广泛的修改,修改,然后最终使人们难以理解并忘记了它。为解决此问题,2008年8月,我发表了一篇文章http://www.webreference.com/programming/javascript/jkm3/3.html,其中介绍了该算法,并将其用作简单的通用JavaScript优化示例。到目前为止,Web Reference已从本文中清除了我的联系信息,甚至我的名字。再一次,该算法已被广泛地修改,修改,然后被人们理解并被很大程度上遗忘。

约瑟夫·迈尔斯(Joseph Myers)的原始字符串重复/乘法JavaScript算法,大约在2000年左右,作为Text.js中的文本乘法函数;通过Web参考以这种形式在2008年8月发布:http :
//www.webreference.com/programming/javascript/jkm3/3.html(本文使用该函数作为JavaScript优化的示例,这是唯一的奇怪方法名称“ 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;
}

Within two months after publication of that article, this same question was posted to Stack Overflow and flew under my radar until now, when apparently the original algorithm for this problem has once again been forgotten. The best solution available on this Stack Overflow page is a modified version of my solution, possibly separated by several generations. Unfortunately, the modifications ruined the solution's optimality. In fact, by changing the structure of the loop from my original, the modified solution performs a completely unneeded extra step of exponential duplicating (thus joining the largest string used in the proper answer with itself an extra time and then discarding it).

Below ensues a discussion of some JavaScript optimizations related to all of the answers to this problem and for the benefit of all.

技术:避免引用对象或对象属性

为了说明该技术的工作原理,我们使用了一个真实的JavaScript函数,该函数创建所需长度的字符串。正如我们将看到的,可以添加更多优化!

类似于此处使用的功能是创建填充以对齐文本列,格式化货币或将块数据填充到边界。文本生成功能还允许输入可变长度,以测试对文本进行操作的任何其他功能。此功能是JavaScript文本处理模块的重要组件之一。

在进行过程中,我们将介绍另外两种最重要的优化技术,同时将原始代码开发为用于创建字符串的优化算法。最终的结果是我在所有地方都使用过的具有工业强度的高性能功能-在JavaScript订单,数据格式和电子邮件/文本消息格式以及许多其他用途中调整商品价格和总计。

用于创建字符串的原始代码 stringFill1()

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

语法很清楚。如您所见,在进行更多优化之前,我们已经使用了局部函数变量。

请注意,s.length在代码中有一个单纯的引用对象属性会影响其性能。更糟糕的是,通过假定读者了解JavaScript字符串对象的属性,使用此对象属性会降低程序的简单性。

使用此对象属性会破坏计算机程序的通用性。该程序假定该x字符串必须是长度为一的字符串。这限制了该stringFill1()功能的应用范围,除了重复单个字符外。如果单个字符包含HTML实体之类的多个字节,则即使它们不能使用&nbsp;

由于不必要使用对象属性而导致的最严重问题是,如果在空的输入字符串上进行测试,该函数将创建一个无限循环x要检查通用性,请将程序应用于尽可能少的输入量。当要求超过可用内存量而崩溃的程序有一个借口。像这样的程序在被要求不产生任何东西时会崩溃,这是不可接受的。有时漂亮的代码是有害代码。

简单性可能是计算机编程的一个模糊目标,但通常不是。当程序缺乏合理的通用性时,就说“该程序就其本身而言已经足够好了”是无效的。如您所见,使用该string.length属性会阻止该程序在常规设置下运行,并且实际上,错误的程序已准备就绪,可能导致浏览器或系统崩溃。

有没有办法改善此JavaScript的性能以及解决这两个严重问题?

当然。只需使用整数。

用于创建字符串的优化代码 stringFill2()

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

时序代码进行比较stringFill1()stringFill2()

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); 

迄今为止的成功 stringFill2()

stringFill1()填充一个100字节的字符串需要47.297微秒(百万分之一秒),而stringFill2()完成相同操作需要27.68微秒。通过避免引用对象属性,性能几乎翻了一番。

技术:避免在长字符串中添加短字符串

我们以前的结果看起来不错-实际上很好。stringFill2()由于使用了前两个优化,因此改进后的功能要快得多。如果我告诉您可以将其改进的速度比现在快很多倍,您会相信吗?

是的,我们可以实现这一目标。现在,我们需要解释如何避免将短字符串附加到长字符串。

与我们的原始功能相比,短期行为似乎还不错。计算机科学家喜欢分析函数或计算机程序算法的“渐近行为”,这意味着通过使用较大的输入进行测试来研究其长期行为。有时,如果不做进一步的测试,就永远不会意识到可以改进计算机程序的方式。为了看到会发生什么,我们将创建一个200字节的字符串。

出现的问题 stringFill2()

使用计时功能,我们发现200字节字符串的时间增加到62.54微秒,而100字节字符串的时间增加到27.68微秒。看起来工作时间应该翻倍,但工作量却翻了三倍或四倍。从编程经验来看,这个结果似乎很奇怪,因为如果有的话,由于工作效率更高(每个函数调用200个字节,而不是每个函数调用100个字节),因此该函数应该稍微快一些。此问题与JavaScript字符串的阴险属性有关:JavaScript字符串是“不可变的”。

不可变意味着一旦创建字符串便无法更改。通过一次添加一个字节,我们不会再消耗一个字节的精力。我们实际上是在重新创建整个字符串再加上一个字节。

实际上,要在100字节的字符串中再增加一个字节,则需要101字节的工作量。让我们简要分析创建N字节字符串的计算成本添加第一个字节的成本为1个计算单位。添加第二个字节的成本不是一个单位而是2个单位(将第一个字节复制到新的字符串对象以及添加第二个字节)。第三个字节的成本为3个单位,依此类推。

C(N) = 1 + 2 + 3 + ... + N = N(N+1)/2 = O(N^2)该符号O(N^2)的发音为N平方的大O,意味着从长远来看,计算量与字符串长度的平方成正比。创建100个字符需要10,000个工作单位,而创建200个字符需要40,000个工作单位。

这就是为什么创建200个字符比使用100个字符花费两倍多的时间的原因。实际上,它应该花费四倍的时间。我们的编程经验是正确的,因为对于较长的字符串,工作效率更高一些,因此只花费了大约三倍的时间。一旦函数调用的开销对于我们要创建的字符串的长度可以忽略不计,创建一个字符串的两倍的时间实际上将花费四倍的时间。

(历史记录:这种分析未必适用于源代码中的字符串,例如html = 'abcd\n' + 'efgh\n' + ... + 'xyz.\n',因为JavaScript源代码编译器可以在将字符串组合成JavaScript字符串对象之前将它们连接在一起。就在几年前,KJS实现当加载带有加号的长字符串源代码时,JavaScript会冻结或崩溃,因为计算时间很短,O(N^2)所以使Web页面超载Konqueror Web浏览器或使用KJS JavaScript引擎核心的Safari并不困难。当我开发标记语言和JavaScript标记语言解析器时遇到了这个问题,然后我为JavaScript Includes编写脚本时发现了导致此问题的原因。)

显然,这种性能的快速下降是一个巨大的问题。鉴于我们无法更改JavaScript将字符串作为不可变对象处理的方式,我们该如何处理呢?解决方案是使用一种算法,该算法将字符串重新创建的次数尽可能少。

为了明确起见,我们的目标是避免在长字符串中添加短字符串,因为要添加短字符串,还必须复制整个长字符串。

该算法如何避免在短字符串中添加短字符串

这是减少新字符串对象创建次数的好方法。将较长的字符串连接在一起,以便一次将一个以上的字节添加到输出中。

例如,要创建一个长度为string的字符串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 */

为此,需要创建一个长度为1的字符串,创建一个长度为2的字符串,创建一个长度为4的字符串,创建一个长度为8的字符串,最后创建一个长度为9的字符串。我们节省了多少成本?

旧费用C(9) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 9 = 45

新成本C(9) = 1 + 2 + 4 + 8 + 9 = 24

请注意,我们必须将长度为1的字符串添加到长度为0的字符串,然后将长度为1的字符串添加为长度为1的字符串,然后将长度为2的字符串添加为长度为2的字符串,然后是长度为4的字符串到长度为4的字符串,然后是长度为8的字符串到长度为1的字符串,以获得长度为9的字符串。我们正在做的事情可以概括为避免在长字符串或其他字符串中添加短字符串单词,尝试将长度相等或几乎相等的字符串连接在一起。

对于旧的计算成本,我们找到了一个公式N(N+1)/2有新成本公式吗?是的,但是很复杂。重要的是它是O(N),因此将字符串长度加倍将使工作量大约增加一倍,而不是四倍。

实现此新想法的代码几乎与计算成本的公式一样复杂。阅读时,请记住这>>= 1意味着向右移1个字节。因此,如果n = 10011是二进制数,则n >>= 1结果为n = 1001

您可能无法识别的代码的另一部分是bitwise和operator &n & 1如果最后一个二进制数字n为1,表达式的计算结果为true;如果最后一个二进制数字n为0 表达式的计算结果为false

新的高效stringFill3()功能

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

未经训练的人看起来很丑陋,但它的表现无非是可爱。

让我们看看该功能的执行情况。在看到结果之后,您可能永远不会忘记O(N^2)算法与O(N)算法之间的区别

stringFill1()创建一个200字节的字符串需要88.7微秒(百万分之一秒),stringFill2()需要62.54,stringFill3()仅需要4.608。是什么使该算法如此出色?所有功能都利用了局部函数变量,但是利用第二和第三种优化技术使的性能提高了20倍stringFill3()

更深入的分析

是什么让这一特殊功能将竞争从水里吹了出来?

如前所述,这两个函数stringFill1()和都stringFill2()运行缓慢的原因是JavaScript字符串是不可变的。无法重新分配内存以允许一次将一个以上的字节附加到JavaScript存储的字符串数据中。每次在字符串末尾再增加一个字节,整个字符串将从头到尾重新生成。

因此,为了提高脚本的性能,必须预先通过将两个字符串连接在一起来预先计算较长的字符串,然后递归地建立所需的字符串长度。

例如,要创建一个16个字母的字符串,首先要预先计算一个2字节的字符串。然后,两个字节的字符串将被重新使用以预先计算一个四字节的字符串。然后,四字节字符串将被重用以预先计算八字节字符串。最后,两个八字节字符串将被重用以创建所需的16字节新字符串。总共必须创建四个新字符串,长度2之一,长度4之一,长度8之一,长度16之一。总成本为2 + 4 + 8 + 16 = 30。

从长远来看,可以通过以相反的顺序相加并使用从第一项a1 = N开始并具有公共比率r = 1/2的几何级数来计算效率。几何级数的总和由给出a_1 / (1-r) = 2N

这比添加一个字符来创建长度为2的新字符串更有效,然后创建长度为3、4、5等的新字符串,直到16。以前的算法使用的是一次添加一个字节的过程。 ,其总成本为n (n + 1) / 2 = 16 (17) / 2 = 8 (17) = 136

显然,136比30大得多,因此以前的算法花费很多得多的时间来构建字符串。

要比较这两种方法,您可以看到递归算法(也称为“分而治之”)在长度为123,457的字符串上的速度要快多少。在我的FreeBSD计算机上,在stringFill3()函数中实现的该算法在0.001058秒内创建了字符串,而原始stringFill1()函数在0.0808秒内创建了字符串。新功能快了76倍。

字符串的长度越大,性能差异就越大。在创建越来越大的字符串的极限中,原始函数的行为大致类似于C1(恒定)时间N^2,而新函数的行为类似于C2(恒定)时间N

从我们的实验中,我们可以确定C1beC1 = 0.0808 / (123457)2 = .00000000000530126997的值和C2be的值C2 = 0.001058 / 123457 = .00000000856978543136在10秒内,新函数可以创建一个包含1,166,890,359个字符的字符串。为了创建相同的字符串,旧功能将需要7,218,384秒的时间。

与十秒钟相比,这几乎是三个月!

我之所以仅回答(迟了几年),是因为我对这个问题的最初解决方案已经在Internet上流传了10多年,而且显然仍然为数不多的记得它的人很少理解。我认为,通过在此处撰写有关该文章的内容,我将对您有所帮助:

高速JavaScript的性能优化/第3页

不幸的是,这里介绍的其他一些解决方案仍然是一些需要三个月才能产生与适当解决方案在10秒内产生的输出量相同的输出的解决方案。

我想花时间在此处重现本文的部分内容,作为有关Stack Overflow的规范答案。

请注意,此处表现最佳的算法显然是基于我的算法,并且可能是从其他人的第三代或第四代适应中继承而来的。不幸的是,这些修改导致其性能下降。此处提出的解决方案的变体可能无法理解我的令人困惑的for (;;)表达式,该表达式看起来像是用C语言编写的服务器的主无限循环,并且仅设计为允许精心定位的break语句进行循环控制,这是最紧凑的方法避免以指数方式将字符串额外花费不必要的时间。

这是非常有效的

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

好消息!String.prototype.repeat现在的JavaScript的一部分

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

除Internet Explorer和Android Webview之外,所有主要浏览器均支持该方法。有关最新列表,请参见MDN:String.prototype.repeat>浏览器兼容性

MDN具有不支持浏览器的polyfill

String.prototype.repeat现在是ES6标准。

'abc'.repeat(3); //abcabcabc

扩展P.Bailey的解决方案

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

这样,您应该可以避免意外的参数类型:

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

编辑:感谢jerone的优雅++num主意!

Array(N+1).join("string_to_repeat")

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

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

这是使用定界符多次重复字符串的方法。

这是对disfated答案的5-7%的改善。

通过在处停止count > 1展开循环来result += pattnern展开循环。这样可以避免最终未使用的循环,pattern += pattern而不必使用昂贵的if-check。最终结果将如下所示:

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;
};

以下是已发行版本的dis贬不一的小提琴:http : //jsfiddle.net/wsdfg/

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

各种方法的测试:

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));
    }
}

这是JSLint安全版本

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

对于所有浏览器

这简直就是简洁:

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

如果您还关心性能,那么这是一种更好的方法:

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

如果要比较两个选项的性能,请参阅此Fiddle此Fiddle进行基准测试。在我自己的测试中,第二个选项在Firefox中大约快2倍,在Chrome中大约快4倍!

仅适用于现代浏览器:

在现代浏览器中,您现在还可以执行以下操作:

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

此选项不仅比其他两个选项都短,而且比第二个选项还要快

不幸的是,它不适用于任何版本的Internet Explorer。表格中的数字指定了完全支持该方法的第一个浏览器版本:

在此处输入图片说明

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

您可以在JSFiddle上对其进行测试Array.join大致来说,针对骇客和雷电的基准测试速度要快10倍(Chrome)至100倍(Safari)至200倍(Firefox)(取决于浏览器)。

只是另一个重复功能:

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

ES2015已经实现了这种repeat()方法!

http://www.ecma-international.org/ecma-262/6.0/#sec-string.prototype.repeat
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/字符串/重复
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

这可能是最小的递归方法:

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

小提琴:http : //jsfiddle.net/3Y9v2/

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

简单递归串联

我只是想给它一个bash,然后这样做:

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

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

我不能说我考虑了很多,它可能表明:-)

可以说更好

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

"foo".ditto( 128 );

这很像已经发布的答案-我知道这一点。

但是为什么要递归呢?

以及一些默认行为呢?

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

"foo".ditto();

因为,尽管非递归方法将处理任意大的重复而不会达到调用堆栈限制,但它要慢得多。

我为什么要烦恼添加更多不那么聪明的方法已经发布的

一方面出于我的娱乐目的,另一方面以一种最简单的方式指出,我知道有很多方法可以给猫剥皮,并且根据情况的不同,显然最好的方法很可能并不理想。

相对快速和复杂的方法在某些情况下可能有效地崩溃和燃烧,而较慢,更简单的方法可能最终完成工作。

有些方法可能会多一点漏洞,因此容易被固定不存在了,而其他的方法可以在任何条件下做工精美,但结构是这样的一个根本不知道它是如何工作。

“那如果我不知道它是如何工作的呢?!”

认真吗

JavaScript具有其最大的优势之一。它对不良行为具有很高的容忍度,并且如此灵活,它会向后弯腰以返回结果,如果它对每个人来说都可能更好的话!

“强大的力量伴随着巨大的责任” ;-)

但更重要和更重要的是,尽管像这样的一般性问题的确会以一种巧妙的回答的形式带来令人敬畏的回答:如果不做其他事情,就可以扩大自己的知识和视野,最后,手头的任务-使用所得方法的实用脚本-可能需要少一点,或者多一点聪明比建议。

这些“完美”的算法非常有趣,但“一种尺寸适合所有”量身定做几乎不可能比量身定制的更好。

这篇讲道是由于缺乏睡眠和短暂的兴趣而带给您的。继续编码!

首先,OP的问题似乎与简洁有关(我理解为“简单易读”,而大多数答案似乎与效率有关),这显然不是一回事,而且我认为除非您实施一些非常特定的大数据操作算法,当您实现基本的数据操作Javascript函数时,请不要担心。简洁更重要。

其次,正如AndréLaszlo所指出的那样,String.repeat是ECMAScript 6的一部分,并且已经在几种流行的实现中提供-因此,最简洁的实现String.repeat是不实现它;-)

最后,如果您需要支持不提供ECMAScript 6实现的主机,那么AndréLaszlo提到的MDN polyfill简明扼要。

因此,事不宜迟,这是我简洁的polyfill:

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

是的,这是递归。我喜欢递归-它们很简单,如果正确完成,则易于理解。关于效率,如果语言支持,那么如果编写正确,它们将非常高效。

根据我的测试,此方法比该Array.join方法快60%尽管显然没有实现它的实现,但它比两者都简单得多。

我的测试设置是节点“ v0.10”,它使用“严格模式”(我认为它启用了某种TCO),repeat(1000)一百万次调用10个字符串。

如果您认为所有这些原型定义,数组创建和联接操作都是过大的,只需在需要的地方使用一行代码即可。字符串S重复N次:

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

将Lodash用于Javascript实用程序功能,例如重复字符串。

Lodash具有出色的性能和ECMAScript兼容性。

我强烈建议将其用于UI开发,并且在服务器端也很有效。

以下是使用Lodash将字符串“ yo”重复两次的方法:

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

使用分而治之的递归解决方案:

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); }
}

我是随机来这里的,从来没有理由在JavaScript中重复一个字符。

artistoex的处理方式和结果给我留下了深刻的印象。正如Dennis所指出的那样,我注意到最后一个字符串concat是不必要的。

将采样偏差放在一起时,我注意到了其他一些内容。

结果变化很大,通常有利于最后一次运行,类似的算法通常会争夺位置。我更改的一件事是不使用JSLitmus生成的count作为调用的种子;由于各种方法产生的计数不同,因此我添加了一个索引。这使事情变得更加可靠。然后,我查看了如何确保将大小不同的字符串传递给函数。这避免了我看到的某些变化,其中某些算法在单个字符或较小的字符串上表现更好。但是,不管字符串大小如何,前3种方法都效果很好。

分叉测试仪

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();

然后,我包括了丹尼斯(Dennis)的修复程序,并决定看看是否可以找到一种方法来寻求更多信息。

由于javascript不能真正优化事物,因此提高性能的最佳方法是手动避免事物。如果我从循环中取出前4个琐碎的结果,则可以避免2-4个字符串存储并将最终存储直接写入结果。

// 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;
}

与Dennis的修复相比,平均提高了1-2%。但是,不同的运行方式和不同的浏览器将显示出足够的差异,以至于与前面的2种算法相比,这种额外的代码可能不值得花精力。

图表

编辑:我主要是在chrome下执行此操作。Firefox和IE经常会偏爱Dennis。

简单方法:

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

人们过度复杂化或浪费性能。数组?递归?你在开玩笑吧。

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

编辑。我进行了一些简单的测试,以与artistoex / disfated和其他人发布的按位版本进行比较。后者仅稍快一些,但内存效率更高。对于“ blah”一词的1000000次重复,使用简单的串联算法(如上),Node进程增加到46兆字节,而对数算法则只有5.5兆字节。后者绝对是必经之路。为了清楚起见,将其重新发布:

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

根据数字串联字符串。

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));

希望有帮助!

使用ES8,您也可以使用padStartpadEnd为此。例如。

var str = 'cat';
var num = 23;
var size = str.length * num;
"".padStart(size, str) // outputs: 'catcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcatcat'

要重复指定次数的字符串,我们可以使用repeat()JavaScript中的内置方法。

这是一个将以下字符串重复4次的示例:

const name = "king";

const repeat = name.repeat(4);

console.log(repeat);

输出:

"kingkingkingking"

或者我们可以创建自己的repeat()函数版本,如下所示:

function repeat(str, n) {
  if (!str || !n) {
    return;
  }

 let final = "";
  while (n) {
    final += s;
    n--;
  }
  return final;
}

console.log(repeat("king", 3))

(最初发布在https://reactgo.com/javascript-repeat-string/

本文地址:http://javascript.askforanswer.com/zhongfuzifuchuan-javascript.html
文章标签: ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!