优化字符串连接的聚合

更新 – 对于那些具有诙谐心态的人来说,你可以假设,无论函数传递给它,Aggregate仍会产生正常结果,包括在优化的情况下。

我编写了这个程序来构建一个从0到19999的长整数字符串,用逗号分隔。

using System; using System.Linq; using System.Diagnostics; namespace ConsoleApplication5 { class Program { static void Main(string[] args) { const int size = 20000; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); Enumerable.Range(0, size).Select(n => n.ToString()).Aggregate((a, b) => a + ", " + b); stopwatch.Stop(); Console.WriteLine(stopwatch.ElapsedMilliseconds + "ms"); } } } 

当我运行它时,它说:

 5116ms 

超过五秒钟,太可怕了。 当然这是因为每次循环都会复制整个字符串。

但是,如果评论指出一个非常小的变化怎么办?

 using System; using System.Linq; using System.Diagnostics; namespace ConsoleApplication5 { using MakeAggregateGoFaster; //  n.ToString()).Aggregate((a, b) => a + ", " + b); stopwatch.Stop(); Console.WriteLine(stopwatch.ElapsedMilliseconds + "ms"); } } } 

现在,当我运行它时,它说:

 42ms 

超过100倍。

MakeAggregateGoFaster命名空间中有什么?

更新2: 在这里写下我的答案 。

您在命名空间MakeAggregateGoFaster中使用自己的扩展方法“覆盖”System.Linq.Aggregate。

也许专门研究IEnumerable并使用StringBuilder?

也许采用Expression>而不是Func这样它就可以分析表达式树并编译一些使用StringBuilder而不是直接调用函数的代码?

只是猜测。

为什么不使用其他forms的Aggregate?

 Enumerable.Range(0, size ).Aggregate(new StringBuilder(), (a, b) => a.Append(", " + b.ToString()), (a) => a.Remove(0,2).ToString()); 

您可以为种子指定任何类型,在第一个lambda函数中执行所需的任何格式化或自定义调用,然后在第二个lambda函数中自定义输出类型。 内置function已经提供了您所需的灵活性。 我的跑步从1444ms到6ms。

没有回答这个问题,但我认为这里的标准模式是使用StringBuilder或string.Join:

 string.join(", ",Enumerable.Range(0, size).Select(n => n.ToString()).ToArray()) 

我之所以问这是否是一个谜题的原因是因为只要满足所述问题的字母就允许谜题在不同程度上牺牲稳健性。 考虑到这一点,这里有:

解决方案1(立即运行,问题无法validation):

 public static string Aggregate(this IEnumerable l, Func f) { return ""; } 

解决方案2(运行速度与问题一样快,但完全忽略代理):

 public static string Aggregate(this IEnumerable l, Func f) { StringBuilder sb = new StringBuilder(); foreach (string item in l) sb.Append(", ").Append(item); return sb.Remove(0,2).ToString(); } 

那么,这完全取决于MageAggregateGoFaster命名空间中的代码现在不是吗?

此命名空间不是.NET运行时的一部分,因此您已链接到某些自定义代码中。

我个人认为,识别字符串连接或类似的东西,并建立一个列表,或类似,然后分配一个大的StringBuilder并使用Append。

一个肮脏的解决方案是:

 namespace MakeAggregateGoFaster { public static class Extensions { public static String Aggregate(this IEnumerable source, Func fn) { StringBuilder sb = new StringBuilder(); foreach (String s in source) { if (sb.Length > 0) sb.Append(", "); sb.Append(s); } return sb.ToString(); } } } 

脏,因为这段代码在执行您所说的程序体验时,根本不使用函数委托。 但是,它会在我的计算机上将执行时间从大约2800ms降低到11ms,并且仍会产生相同的结果。

现在,下一次,也许你应该问一个真正的问题,而不仅仅是看看我是多么聪明的胸部殴打?