これは不自然に聞こえるかもしれませんが、私は本当に良い説明を見つけることができませんでしたAggregate
。
良いとは、短く、わかりやすく、包括的で、小さくて明確な例を意味します。
これは不自然に聞こえるかもしれませんが、私は本当に良い説明を見つけることができませんでしたAggregate
。
良いとは、短く、わかりやすく、包括的で、小さくて明確な例を意味します。
回答:
の最も理解しやすい定義Aggregate
は、前に行った操作を考慮して、リストの各要素に対して操作を実行することです。つまり、最初の要素と2番目の要素に対してアクションを実行し、結果を転送します。次に、前の結果と3番目の要素に対して演算を実行します。等
例1.数値を合計する
var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // output: 10 (1+2+3+4)
これを追加1
し2
て作り3
ます。次に、3
(前の結果)と3
(シーケンスの次の要素)を追加してmakeを作成し6
ます。次に、6
と4
を追加します10
。
例2.文字列の配列からcsvを作成する
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
これはほとんど同じ方法で機能します。a
カンマを連結しb
て作るa,b
。次にa,b
、コンマと連結しc
てmakeしa,b,c
ます。等々。
例3.シードを使用して数値を乗算する
完全を期すために、シード値をとるオーバーロードがAggregate
あります。
var multipliers = new []{10,20,30,40};
var multiplied = multipliers.Aggregate(5, (a,b) => a * b);
Console.WriteLine(multiplied); //Output 1200000 ((((5*10)*20)*30)*40)
上記の例と同様に、これはの値で始まり5
、シーケンスの最初の要素を乗算して10
、結果をにし50
ます。この結果は繰り越され、シーケンスの次の数値で乗算されて20
の結果が得られ1000
ます。これは、シーケンスの残りの2つの要素まで続きます。
ライブの例:http : //rextester.com/ZXZ64749
ドキュメント:http : //msdn.microsoft.com/en-us/library/bb548651.aspx
補遺
上記の例2では、文字列連結を使用して、コンマで区切られた値のリストを作成しています。これは、Aggregate
この回答の意図であった使用法を説明する単純な方法です。ただし、この手法を使用して実際に大量のコンマ区切りデータを作成する場合はStringBuilder
、を使用する方が適切でありAggregate
、シードされたオーバーロードを使用してを開始することと完全に互換性がありStringBuilder
ます。
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate(new StringBuilder(), (a,b) => {
if(a.Length>0)
a.Append(",");
a.Append(b);
return a;
});
Console.WriteLine(csv);
更新された例:http : //rextester.com/YZCVXV6464
TakeWhile
次にチェーンすることをお勧めしAggregate
ます-それはEnumerable拡張機能の素晴らしい機能です-それらは簡単にチェーン可能です。だからあなたはで終わるTakeWhile(a => a == 'a').Aggregate(....)
。この例をご覧ください:rextester.com/WPRA60543
var csv = string.Join(",", chars)
(集約または文字列ビルダーは不要)。しかし、ええ、答えの要点は集約の使用例を示すことでした。ただし、文字列を結合するだけでは推奨されないことに言及したいと思います。そのための専用の方法がすでにあります...
var biggestAccount = Accounts.Aggregate((a1, a2) => a1.Amount >= a2.Amount ? a1 : a2);
それはあなたが話している過負荷に部分的に依存しますが、基本的な考え方は:
(currentValue, sequenceValue)
する(nextValue)
currentValue = nextValue
currentValue
あなたは見つけるかもしれません Aggregate
私のEdulinqシリーズの投稿が役に立つ -より詳細な説明(さまざまなオーバーロードを含む)と実装が含まれています。
簡単な例の1つはAggregate
、の代わりにを使用していCount
ます。
// 0 is the seed, and for each item, we effectively increment the current value.
// In this case we can ignore "item" itself.
int count = sequence.Aggregate(0, (current, item) => current + 1);
または、文字列のシーケンス内のすべての文字列の長さを合計することもできます。
int total = sequence.Aggregate(0, (current, item) => current + item.Length);
個人的に私が役立つことはめったにありませんAggregate
-「調整された」集約方法は通常私にとって十分です。
スーパーショート アグリゲートは、Haskell / ML / F#のフォールドのように機能します。
少し長い .Max()、. Min()、. Sum()、. Average()はすべて、シーケンス内の要素を反復処理し、それぞれの集約関数を使用してそれらを集約します。.Aggregate()は、開発者が開始状態(別名シード)および集約関数を指定できるようにするという点で、一般化されたアグリゲーターです。
短い説明を求めていたのは知っていますが、他の人がいくつかの短い回答をしていたので、もう少し長い説明に興味があると思いました。
コード付きの長いバージョン 何が行われるかを示す1つの方法は、foreachを1回、.Aggregateを1回使用して、サンプル標準偏差を実装する方法を示すことができます。注:ここではパフォーマンスを優先していないため、不要なコレクションに対して数回繰り返します
最初に、2次距離の合計を作成するために使用されるヘルパー関数:
static double SumOfQuadraticDistance (double average, int value, double state)
{
var diff = (value - average);
return state + diff * diff;
}
次に、ForEachを使用して標準偏差をサンプリングします。
static double SampleStandardDeviation_ForEach (
this IEnumerable<int> ints)
{
var length = ints.Count ();
if (length < 2)
{
return 0.0;
}
const double seed = 0.0;
var average = ints.Average ();
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
return Math.Sqrt (sumOfQuadraticDistance / (length - 1));
}
次に、.Aggregateを使用します。
static double SampleStandardDeviation_Aggregate (
this IEnumerable<int> ints)
{
var length = ints.Count ();
if (length < 2)
{
return 0.0;
}
const double seed = 0.0;
var average = ints.Average ();
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
return Math.Sqrt (sumOfQuadraticDistance / (length - 1));
}
これらの関数は、sumOfQuadraticDistanceの計算方法を除いて同じです。
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
対:
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
したがって、.Aggregateが行うことは、このアグリゲーターパターンをカプセル化することであり、.Aggregateの実装は次のようになると期待しています。
public static TAggregate Aggregate<TAggregate, TValue> (
this IEnumerable<TValue> values,
TAggregate seed,
Func<TAggregate, TValue, TAggregate> aggregator
)
{
var state = seed;
foreach (var value in values)
{
state = aggregator (state, value);
}
return state;
}
標準偏差関数を使用すると、次のようになります。
var ints = new[] {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
var average = ints.Average ();
var sampleStandardDeviation = ints.SampleStandardDeviation_Aggregate ();
var sampleStandardDeviation2 = ints.SampleStandardDeviation_ForEach ();
Console.WriteLine (average);
Console.WriteLine (sampleStandardDeviation);
Console.WriteLine (sampleStandardDeviation2);
私見では
では、.Aggregateは読みやすさを向上させますか?.Where、.Select、.OrderByなどが読みやすさを大いに高めると思うので、一般に私はLINQが好きです(インライン化された階層の.Selectsを避ける場合)。Aggregateは完全性の理由からLinq内にある必要がありますが、個人的には、.Aggregateは適切に記述されたforeachと比較して読みやすさを向上させるとは確信していません。
SampleStandardDeviation_Aggregate()
とSampleStandardDeviation_ForEach()
することはできませんprivate
(アクセス修飾子が存在しない場合にデフォルトでは)、そのいずれかによって計上されている必要がありますpublic
かinternal
、それは私には思われる
注意:
Func<X, Y, R>
はタイプX
との2つの入力を持つ関数で、タイプY
の結果を返しますR
。
Enumerable.Aggregateには3つのオーバーロードがあります。
オーバーロード1:
A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)
例:
new[]{1,2,3,4}.Aggregate((x, y) => x + y); // 10
このオーバーロードは単純ですが、次の制限があります。
InvalidOperationException
ます。オーバーロード2:
B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)
例:
var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n); // 2
このオーバーロードはより一般的です:
bIn
)。オーバーロード3:
C Aggregate<A,B,C>(IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)
3番目のオーバーロードは、あまり有用なIMOではありません。
同じことは、結果を変換する関数が後に続くオーバーロード2を使用することで、より簡潔に書くことができます。
イラストは、この優れたブログポストから採用されました。
Aggegate
、.net のオーバーロードはありませんFunc<T, T, T>
。
seed
は、アキュムレータ関数をN -1回適用することがわかります。他のオーバーロード(それはを受け取るseed
)は、アキュムレータ関数をN回適用します。
集計は、基本的にデータのグループ化または合計に使用されます。
MSDNによると、「集計関数はシーケンスにアキュムレータ関数を適用します。」
例1:すべての数値を配列に追加します。
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);
*重要:デフォルトの初期集計値は、コレクションのシーケンスの1要素です。つまり、変数の初期値の合計はデフォルトで1になります。
変数の説明
合計:関数によって返される合計値(集計値)を保持します。
nextValue:配列シーケンスの次の値です。この値は、合計値、つまり合計に追加されます。
例2:すべての項目を配列に追加します。また、アキュムレータの初期値を設定して、10から追加を開始します。
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
引数の説明:
最初の引数は、配列の次の値で加算を開始するために使用される初期値(開始値、つまりシード値)です。
2番目の引数はfuncであり、2 intを取るfuncです。
1.合計:これは、計算後にfuncによって返される合計値(集約値)の前と同じです。
2.nextValue::配列シーケンスの次の値です。この値は、合計値、つまり合計に追加されます。
また、このコードをデバッグすると、集計がどのように機能するかをよりよく理解できます。
ジャミエックの答えから多くを学びました。
CSV文字列の生成のみが必要な場合は、これを試すことができます。
var csv3 = string.Join(",",chars);
これは100万の文字列を使ったテストです
0.28 seconds = Aggregate w/ String Builder
0.30 seconds = String.Join
ソースコードはこちら
ここでのすべてのすばらしい答えに加えて、私はそれを使用して、一連の変換ステップを通じてアイテムをウォークスルーしました。
変換がとして実装されている場合、Func<T,T>
にいくつかの変換を追加し、List<Func<T,T>>
を使用Aggregate
してのインスタンスT
を各ステップでウォークすることができます。
string
値を取得して、プログラムで構築できる一連のテキスト変換を実行したいとします。
var transformationPipeLine = new List<Func<string, string>>();
transformationPipeLine.Add((input) => input.Trim());
transformationPipeLine.Add((input) => input.Substring(1));
transformationPipeLine.Add((input) => input.Substring(0, input.Length - 1));
transformationPipeLine.Add((input) => input.ToUpper());
var text = " cat ";
var output = transformationPipeLine.Aggregate(text, (input, transform)=> transform(input));
Console.WriteLine(output);
これにより、一連の変換が作成されます。先頭と末尾のスペースを削除->最初の文字を削除->最後の文字を削除->大文字に変換 このチェーンのステップは、必要に応じて追加、削除、または並べ替えて、必要な変換パイプラインを作成できます。
この特定のパイプラインの最終結果は、に" cat "
なり"A"
ます。
これは何でT
もよいことに気づくと、非常に強力になる可能性があります。これはBitMap
、例として、フィルターのような画像変換に使用できます。
定義
集約メソッドは、ジェネリックコレクションの拡張メソッドです。Aggregateメソッドは、コレクションの各アイテムに関数を適用します。関数を適用するだけでなく、その結果を次の反復の初期値として使用します。したがって、結果として、コレクションから計算値(最小、最大、平均、またはその他の統計値)を取得します。
したがって、Aggregateメソッドは再帰関数の安全な実装の形式です。
再帰はコレクションの各項目を反復し、誤った終了条件によって無限ループの中断を取得できないため、安全です。現在の関数の結果が次の関数呼び出しのパラメーターとして使用されるため、再帰的。
構文:
collection.Aggregate(seed, func, resultSelector);
使い方:
var nums = new[]{1, 2};
var result = nums.Aggregate(1, (result, n) => result + n); //result = (1 + 1) + 2 = 4
var result2 = nums.Aggregate(0, (result, n) => result + n, response => (decimal)response/2.0); //result2 = ((0 + 1) + 2)*1.0/2.0 = 3*1.0/2.0 = 3.0/2.0 = 1.5
実用的な使い方:
int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);
これはこの関数と同じことをしています:
public static int Factorial(int n)
{
if (n < 1) return 1;
return n * Factorial(n - 1);
}
var numbers = new[]{3, 2, 6, 4, 9, 5, 7};
var avg = numbers.Aggregate(0.0, (result, x) => result + x, response => (double)response/(double)numbers.Count());
var min = numbers.Aggregate((result, x) => (result < x)? result: x);
var path = @“c:\path-to-folder”;
string[] txtFiles = Directory.GetFiles(path).Where(f => f.EndsWith(“.txt”)).ToArray<string>();
var output = txtFiles.Select(f => File.ReadAllText(f, Encoding.Default)).Aggregate<string>((result, content) => result + content);
File.WriteAllText(path + “summary.txt”, output, Encoding.Default);
Console.WriteLine(“Text files merged into: {0}”, output); //or other log info
これは、Aggregate
Linq SortingなどのFluent APIでの使用に関する説明です。
var list = new List<Student>();
var sorted = list
.OrderBy(s => s.LastName)
.ThenBy(s => s.FirstName)
.ThenBy(s => s.Age)
.ThenBy(s => s.Grading)
.ThenBy(s => s.TotalCourses);
そして、一連のフィールドを取得するソート関数を実装したいことを見てみましょう。これはAggregate
、次のようにforループの代わりに使用すると非常に簡単です。
public static IOrderedEnumerable<Student> MySort(
this List<Student> list,
params Func<Student, object>[] fields)
{
var firstField = fields.First();
var otherFields = fields.Skip(1);
var init = list.OrderBy(firstField);
return otherFields.Skip(1).Aggregate(init, (resultList, current) => resultList.ThenBy(current));
}
そして、次のように使用できます。
var sorted = list.MySort(
s => s.LastName,
s => s.FirstName,
s => s.Age,
s => s.Grading,
s => s.TotalCourses);
誰もが彼の説明をしました。私の説明はそのようです。
Aggregateメソッドは、コレクションの各アイテムに関数を適用します。たとえば、コレクション{6、2、8、3}と、関数Add(operator +)を実行して(((6 + 2)+8)+3)とし、19を返します。
var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: (result, item) => result + item);
// sum: (((6+2)+8)+3) = 19
この例では、ラムダ式の代わりに名前付きメソッドAddが渡されます。
var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: Add);
// sum: (((6+2)+8)+3) = 19
private static int Add(int x, int y) { return x + y; }
多次元整数配列の列を合計するために使用される集計
int[][] nonMagicSquare =
{
new int[] { 3, 1, 7, 8 },
new int[] { 2, 4, 16, 5 },
new int[] { 11, 6, 12, 15 },
new int[] { 9, 13, 10, 14 }
};
IEnumerable<int> rowSums = nonMagicSquare
.Select(row => row.Sum());
IEnumerable<int> colSums = nonMagicSquare
.Aggregate(
(priorSums, currentRow) =>
priorSums.Select((priorSum, index) => priorSum + currentRow[index]).ToArray()
);
Select with indexはAggregate func内で使用され、一致する列を合計して新しい配列を返します。{3 + 2 = 5、1 + 4 = 5、7 + 16 = 23、8 + 5 = 13}。
Console.WriteLine("rowSums: " + string.Join(", ", rowSums)); // rowSums: 19, 27, 44, 46
Console.WriteLine("colSums: " + string.Join(", ", colSums)); // colSums: 25, 24, 45, 42
しかし、ブール型配列の真の数を数えることは、蓄積された型(int)がソース型(bool)と異なるため、より困難です。ここでは、2番目のオーバーロードを使用するためにシードが必要です。
bool[][] booleanTable =
{
new bool[] { true, true, true, false },
new bool[] { false, false, false, true },
new bool[] { true, false, false, true },
new bool[] { true, true, false, false }
};
IEnumerable<int> rowCounts = booleanTable
.Select(row => row.Select(value => value ? 1 : 0).Sum());
IEnumerable<int> seed = new int[booleanTable.First().Length];
IEnumerable<int> colCounts = booleanTable
.Aggregate(seed,
(priorSums, currentRow) =>
priorSums.Select((priorSum, index) => priorSum + (currentRow[index] ? 1 : 0)).ToArray()
);
Console.WriteLine("rowCounts: " + string.Join(", ", rowCounts)); // rowCounts: 3, 1, 2, 2
Console.WriteLine("colCounts: " + string.Join(", ", colCounts)); // colCounts: 3, 2, 1, 2
[1,2,3,4]
でしょう[3,3,4]
、その後[6,4]
、最後に[10]
。ただし、単一の値の配列を返す代わりに、値そのものを取得します。