更新 - 对于那些有思想心态的人,你可以假设,无论函数传递给它,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; // <---- inserted this
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");
}
}
}
现在,当我运行它时,它说:
42ms
快了100多倍。
MakeAggregateGoFaster命名空间中有什么?
更新2: Wrote up my answer here。
答案 0 :(得分:43)
为什么不使用其他形式的聚合?
Enumerable.Range(0, size ).Aggregate(new StringBuilder(),
(a, b) => a.Append(", " + b.ToString()),
(a) => a.Remove(0,2).ToString());
您可以为种子指定任何类型,在第一个lambda函数中执行所需的任何格式化或自定义调用,然后在第二个lambda函数中自定义输出类型。内置功能已经提供了您所需的灵活性。我的跑步从1444ms到6ms。
答案 1 :(得分:15)
您在命名空间中使用自己的扩展方法“覆盖”System.Linq.Aggregate MakeAggregateGoFaster。
也许专注于IEnumerable<string>
并使用StringBuilder?
也许使用Expression<Func<string, string, string>>
而不是Func<string, string, string>
,这样它可以分析表达式树并编译一些使用StringBuilder而不是直接调用函数的代码?
猜猜。
答案 2 :(得分:5)
没有回答这个问题,但我认为这里的标准模式是使用StringBuilder或string.Join:
string.join(", ",Enumerable.Range(0, size).Select(n => n.ToString()).ToArray())
答案 3 :(得分:4)
我问这是否是一个谜题的原因是因为只要满足所述问题的字母,就允许谜题在不同程度上牺牲稳健性。考虑到这一点,这里有:
解决方案1(立即运行,问题无法验证):
public static string Aggregate(this IEnumerable<string> l, Func<string, string, string> f) {
return "";
}
解决方案2(运行速度与问题一样快,但完全忽略代理):
public static string Aggregate(this IEnumerable<string> l, Func<string, string, string> f) {
StringBuilder sb = new StringBuilder();
foreach (string item in l)
sb.Append(", ").Append(item);
return sb.Remove(0,2).ToString();
}
答案 4 :(得分:3)
那么,这完全取决于MageAggregateGoFaster命名空间中的代码现在不是吗?
此命名空间不是.NET运行时的一部分,因此您已链接了一些自定义代码。
就我个人而言,我认为识别字符串连接或类似内容,构建列表或类似内容,然后分配一个大的StringBuilder并使用Append。
一个肮脏的解决方案是:
namespace MakeAggregateGoFaster
{
public static class Extensions
{
public static String Aggregate(this IEnumerable<String> source, Func<String, String, String> fn)
{
StringBuilder sb = new StringBuilder();
foreach (String s in source)
{
if (sb.Length > 0)
sb.Append(", ");
sb.Append(s);
}
return sb.ToString();
}
}
}
脏,因为这段代码在执行您所说的程序体验时,根本不使用函数委托。但是,它会在我的计算机上将执行时间从大约2800ms降低到11ms,并且仍会产生相同的结果。
现在,下一次,也许你应该问一个真正的问题,而不仅仅是看看我是多么聪明类型的胸部殴打?