19

更新- 对于那些有幽默感的人,您可以假设 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: 在这里写下我的答案

4

5 回答 5

47

为什么不使用其他形式的聚合之一?

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

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

于 2009-01-14T17:18:03.437 回答
15

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

也许专门研究IEnumerable<string>和使用 StringBuilder?

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

只是猜测。

于 2008-12-09T23:30:24.210 回答
5

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

string.Join(", ",Enumerable.Range(0, size).Select(n => n.ToString()).ToArray())
于 2008-12-09T23:16:41.100 回答
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();
}
于 2008-12-09T23:35:02.360 回答
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();
        }
    }
}

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

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

于 2008-12-09T23:15:35.487 回答