19

更新-ファセットな心構えの人にとっては、最適化されている場合を含め、Aggregateに渡された関数が何であれ、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

5秒以上、ひどい。もちろん、それは文字列全体がループの周りで毎回コピーされているためです。

しかし、コメントで示されている非常に小さな変更を1つ行うとどうなりますか?

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

Aggregate の他の形式のいずれかを使用しないのはなぜですか?

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

シードに任意の型を指定し、最初のラムダ関数で必要な書式設定またはカスタム呼び出しを実行してから、2 番目のラムダ関数で出力型をカスタマイズできます。組み込み機能により、必要な柔軟性がすでに提供されています。私の実行は 1444 ミリ秒から 6 ミリ秒になりました。

于 2009-01-14T17:18:03.437 に答える
15

名前空間MakeAggregateGoFasterで独自の拡張メソッドを使用してSystem.Linq.Aggregateを「オーバーライド」しています。

おそらくIEnumerable<string>StringBuilderに特化し、それを利用していますか?

たぶん、Expression<Func<string, string, string>>代わりに取ってFunc<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を1つ割り当てて、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 に答える