Das mag lahm klingen, aber ich konnte keine wirklich gute Erklärung dafür finden Aggregate
.
Gut bedeutet kurz, beschreibend, umfassend mit einem kleinen und klaren Beispiel.
Das mag lahm klingen, aber ich konnte keine wirklich gute Erklärung dafür finden Aggregate
.
Gut bedeutet kurz, beschreibend, umfassend mit einem kleinen und klaren Beispiel.
Antworten:
Die am einfachsten zu verstehende Definition von Aggregate
ist, dass für jedes Element der Liste eine Operation ausgeführt wird, wobei die zuvor durchgeführten Operationen berücksichtigt werden. Das heißt, es führt die Aktion für das erste und zweite Element aus und überträgt das Ergebnis. Dann arbeitet es mit dem vorherigen Ergebnis und dem dritten Element und überträgt es. usw.
Beispiel 1. Zahlen summieren
var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // output: 10 (1+2+3+4)
Dies fügt hinzu 1
und 2
zu machen 3
. Dann werden 3
(Ergebnis des vorherigen) und 3
(nächstes Element in Folge) hinzugefügt, um zu machen 6
. Dann fügt hinzu 6
und 4
zu machen 10
.
Beispiel 2. Erstellen Sie eine CSV aus einem Array von Zeichenfolgen
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
Dies funktioniert ähnlich. Verketten Sie a
ein Komma und b
machen Sie a,b
. Dann a,b
mit einem Komma verketten und c
machen a,b,c
. und so weiter.
Beispiel 3. Multiplizieren von Zahlen mit einem Startwert
Der Vollständigkeit halber ist es eine Überlast von Aggregate
dem einen Ausgangswert annimmt.
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)
Ähnlich wie in den obigen Beispielen beginnt dies mit einem Wert von 5
und multipliziert ihn mit dem ersten Element der Sequenz 10
, das ein Ergebnis von ergibt 50
. Dieses Ergebnis wird übertragen und mit der nächsten Zahl in der Sequenz multipliziert 20
, um ein Ergebnis von zu erhalten 1000
. Dies setzt sich durch die verbleibenden 2 Elemente der Sequenz fort.
Live-Beispiele: http://rextester.com/ZXZ64749
Dokumente: http://msdn.microsoft.com/en-us/library/bb548651.aspx
Nachtrag
In Beispiel 2 oben wird die Verkettung von Zeichenfolgen verwendet, um eine durch Komma getrennte Liste von Werten zu erstellen. Dies ist eine vereinfachte Methode, um die Verwendung Aggregate
dieser Antwort zu erklären . Wenn Sie diese Technik jedoch verwenden, um tatsächlich eine große Menge von durch Kommas getrennten Daten zu erstellen, ist es besser, a zu verwenden StringBuilder
, und dies ist vollständig kompatibel mit der Aggregate
Verwendung der gesetzten Überladung, um die zu initiieren 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);
Aktualisiertes Beispiel: http://rextester.com/YZCVXV6464
TakeWhile
dann Aggregate
- das ist die Pflicht von Enumerable-Erweiterungen - sie sind leicht verkettbar. So enden Sie mit TakeWhile(a => a == 'a').Aggregate(....)
. Siehe dieses Beispiel: rextester.com/WPRA60543
var csv = string.Join(",", chars)
(kein Aggregat oder Stringbuilder erforderlich) - aber ja, ich weiß, der Sinn der Antwort bestand darin, eine beispielhafte Verwendung des Aggregats anzugeben, damit es cool ist. Aber ich wollte immer noch erwähnen, dass es nicht empfohlen wird, nur Strings zu verbinden, dafür gibt es bereits eine Methode ...
var biggestAccount = Accounts.Aggregate((a1, a2) => a1.Amount >= a2.Amount ? a1 : a2);
Es hängt teilweise davon ab, von welcher Überlastung Sie sprechen, aber die Grundidee ist:
(currentValue, sequenceValue)
in die transformiert werden soll(nextValue)
currentValue = nextValue
currentValue
Sie können den Aggregate
Beitrag in meiner Edulinq-Reihe nützlich finden - er enthält eine detailliertere Beschreibung (einschließlich der verschiedenen Überladungen) und Implementierungen.
Ein einfaches Beispiel ist die Verwendung Aggregate
als Alternative zu 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);
Oder vielleicht alle Längen von Strings in einer Folge von Strings summieren:
int total = sequence.Aggregate(0, (current, item) => current + item.Length);
Persönlich finde ich das seltenAggregate
nützlich - die "maßgeschneiderten" Aggregationsmethoden sind normalerweise gut genug für mich.
Superkurzes Aggregat funktioniert wie Fold in Haskell / ML / F #.
Etwas länger .Max (), .Min (), .Sum (), .Average () durchlaufen alle Elemente in einer Sequenz und aggregieren sie mit der jeweiligen Aggregatfunktion. .Aggregate () ist ein verallgemeinerter Aggregator, der es dem Entwickler ermöglicht, den Startstatus (auch bekannt als Seed) und die Aggregatfunktion anzugeben.
Ich weiß, dass Sie um eine kurze Erklärung gebeten haben, aber ich dachte, als andere ein paar kurze Antworten gaben, dachte ich, dass Sie vielleicht an einer etwas längeren interessiert sein würden
Lange Version mit Code Eine Möglichkeit, um zu veranschaulichen, was dies bedeutet, besteht darin, zu zeigen, wie Sie die Beispielstandardabweichung einmal mit foreach und einmal mit .Aggregate implementieren. Hinweis: Ich habe die Leistung hier nicht priorisiert, daher iteriere ich unnötig mehrmals über die Sammlung
Zuerst eine Hilfsfunktion, mit der eine Summe quadratischer Abstände erstellt wird:
static double SumOfQuadraticDistance (double average, int value, double state)
{
var diff = (value - average);
return state + diff * diff;
}
Dann probieren Sie die Standardabweichung mit 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));
}
Dann einmal mit .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));
}
Beachten Sie, dass diese Funktionen identisch sind, außer wie sumOfQuadraticDistance berechnet wird:
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
Gegen:
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
Was .Aggregate also tut, ist, dass es dieses Aggregatormuster kapselt, und ich gehe davon aus, dass die Implementierung von .Aggregate ungefähr so aussehen würde:
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;
}
Die Verwendung der Standardabweichungsfunktionen würde ungefähr so aussehen:
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);
meiner bescheidenen Meinung nach
Hilft .Aggregate also bei der Lesbarkeit? Im Allgemeinen liebe ich LINQ, weil ich denke, dass .Where, .Select, .OrderBy usw. die Lesbarkeit erheblich verbessern (wenn Sie inlinierte hierarhische .Selects vermeiden). Aggregate muss aus Gründen der Vollständigkeit in Linq sein, aber ich persönlich bin nicht so überzeugt, dass .Aggregate die Lesbarkeit im Vergleich zu einem gut geschriebenen Foreach erhöht.
SampleStandardDeviation_Aggregate()
und SampleStandardDeviation_ForEach()
können nicht sein private
(standardmäßig in Abwesenheit eines Zugriffsqualifizierers), sollten also entweder public
oder internal
, wie mir scheint
Erinnerung:
Func<X, Y, R>
ist eine Funktion mit zwei Eingaben vom TypX
undY
, die ein Ergebnis vom Typ zurückgibtR
.
Enumerable.Aggregate hat drei Überladungen:
Überladung 1:
A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)
Beispiel:
new[]{1,2,3,4}.Aggregate((x, y) => x + y); // 10
Diese Überlastung ist einfach, weist jedoch die folgenden Einschränkungen auf:
InvalidOperationException
.Überladung 2:
B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)
Beispiel:
var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n); // 2
Diese Überlastung ist allgemeiner:
bIn
).Überladung 3:
C Aggregate<A,B,C>(IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)
Die dritte Überlastung ist IMO nicht sehr nützlich.
Dasselbe kann prägnanter geschrieben werden, indem Überladung 2 gefolgt von einer Funktion verwendet wird, die das Ergebnis transformiert.
Die Illustrationen stammen aus diesem ausgezeichneten Blogpost .
Aggegate
in .net, die eine benötigt Func<T, T, T>
.
seed
, die Akkumulatorfunktion N -1 mal anwendet ; während die anderen Überlastungen (das sich ein Take seed
) gelten die Akkumulatorfunktion N mal.
Aggregat wird grundsätzlich zum Gruppieren oder Zusammenfassen von Daten verwendet.
Laut MSDN "Aggregatfunktion Wendet eine Akkumulatorfunktion über eine Sequenz an."
Beispiel 1: Fügen Sie alle Zahlen in einem Array hinzu.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);
* wichtig: Der anfängliche Aggregatwert ist standardmäßig das 1-Element in der Erfassungssequenz. dh: Der Anfangswert der Gesamtvariablen ist standardmäßig 1.
variable Erklärung
total: Es enthält den von der Funktion zurückgegebenen Summenwert (aggregierter Wert).
nextValue: Dies ist der nächste Wert in der Array-Sequenz. Dieser Wert wird dann zum aggregierten Wert addiert, dh insgesamt.
Beispiel 2: Fügen Sie alle Elemente in einem Array hinzu. Stellen Sie außerdem den anfänglichen Akkumulatorwert so ein, dass ab 10 mit dem Hinzufügen begonnen wird.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
Argumente Erklärung:
Das erste Argument ist der Anfangsbuchstabe (Startwert, dh Startwert), mit dem die Addition mit dem nächsten Wert im Array gestartet wird.
Das zweite Argument ist eine Funktion, die 2 int benötigt.
1.total: Dies gilt genauso wie vor dem Summenwert (aggregierter Wert), der von der Funktion nach der Berechnung zurückgegeben wird.
2.nextValue :: Dies ist der nächste Wert in der Array-Sequenz. Dieser Wert wird dann zum aggregierten Wert addiert, dh insgesamt.
Wenn Sie diesen Code auch debuggen, erhalten Sie ein besseres Verständnis für die Funktionsweise von Aggregaten.
Ich habe viel aus Jamiecs Antwort gelernt .
Wenn Sie nur eine CSV-Zeichenfolge generieren müssen, können Sie dies versuchen.
var csv3 = string.Join(",",chars);
Hier ist ein Test mit 1 Million Zeichenfolgen
0.28 seconds = Aggregate w/ String Builder
0.30 seconds = String.Join
Der Quellcode ist hier
Zusätzlich zu all den tollen Antworten hier habe ich es auch verwendet, um einen Gegenstand durch eine Reihe von Transformationsschritten zu führen.
Wenn eine Transformation als a implementiert ist Func<T,T>
, können Sie a mehrere Transformationen hinzufügen List<Func<T,T>>
und Aggregate
eine Instanz von T
durch jeden Schritt führen.
Sie möchten einen string
Wert nehmen und ihn durch eine Reihe von Texttransformationen führen, die programmgesteuert erstellt werden können.
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);
Dadurch wird eine Kette von Transformationen erstellt: Entfernen Sie führende und nachfolgende Leerzeichen -> entfernen Sie das erste Zeichen -> entfernen Sie das letzte Zeichen -> konvertieren Sie in Großbuchstaben. Schritte in dieser Kette können nach Bedarf hinzugefügt, entfernt oder neu angeordnet werden, um die erforderliche Transformationspipeline zu erstellen.
Das Endergebnis dieser speziellen Pipeline, ist , dass " cat "
wird "A"
.
Dies kann sehr mächtig werden, sobald Sie erkennen, dass T
dies alles sein kann . Dies könnte für Bildtransformationen wie Filter am BitMap
Beispiel verwendet werden.
Definition
Die Aggregatmethode ist eine Erweiterungsmethode für generische Sammlungen. Die Aggregatmethode wendet eine Funktion auf jedes Element einer Sammlung an. Wendet nicht nur eine Funktion an, sondern nimmt ihr Ergebnis als Anfangswert für die nächste Iteration. Als Ergebnis erhalten wir einen berechneten Wert (min, max, avg oder einen anderen statistischen Wert) aus einer Sammlung.
Daher ist die Aggregatmethode eine Form der sicheren Implementierung einer rekursiven Funktion.
Sicher , da die Rekursion über jedes Element einer Sammlung iteriert und wir durch eine falsche Ausgangsbedingung keine Endlosschleifenaufhängung erhalten können. Rekursiv , da das Ergebnis der aktuellen Funktion als Parameter für den nächsten Funktionsaufruf verwendet wird.
Syntax:
collection.Aggregate(seed, func, resultSelector);
Wie es funktioniert:
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
Praktische Anwendung:
int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);
das macht das gleiche wie diese Funktion:
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
Dies ist eine Erklärung zur Verwendung Aggregate
in einer Fluent-API wie Linq Sorting.
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);
und lassen Sie uns sehen, dass wir eine Sortierfunktion implementieren möchten, die eine Reihe von Feldern verwendet. Dies ist sehr einfach Aggregate
anstelle einer for-Schleife wie folgt :
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));
}
Und wir können es so verwenden:
var sorted = list.MySort(
s => s.LastName,
s => s.FirstName,
s => s.Age,
s => s.Grading,
s => s.TotalCourses);
Jeder hat seine Erklärung gegeben. Meine Erklärung ist so.
Die Aggregatmethode wendet eine Funktion auf jedes Element einer Sammlung an. Nehmen wir zum Beispiel die Sammlung {6, 2, 8, 3} und die Funktion Add (Operator +), die sie ausführt (((6 + 2) +8) +3) und gibt 19 zurück
var numbers = new List<int> { 6, 2, 8, 3 };
int sum = numbers.Aggregate(func: (result, item) => result + item);
// sum: (((6+2)+8)+3) = 19
In diesem Beispiel wird anstelle des Lambda-Ausdrucks die benannte Methode Add übergeben.
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; }
Eine kurze und wesentliche Definition könnte folgende sein: Die Linq Aggregate-Erweiterungsmethode ermöglicht es, eine Art rekursive Funktion zu deklarieren, die auf die Elemente einer Liste angewendet wird, deren Operanden zwei sind: die Elemente in der Reihenfolge, in der sie in der Liste vorhanden sind, jeweils ein Element und das Ergebnis der vorherigen rekursiven Iteration oder nichts, wenn noch keine Rekursion.
Auf diese Weise können Sie die Fakultät für Zahlen berechnen oder Zeichenfolgen verketten.
Aggregat zum Summieren von Spalten in einem mehrdimensionalen Ganzzahlarray
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()
);
Mit Index auswählen wird in der Aggregatfunktion verwendet, um die übereinstimmenden Spalten zu summieren und ein neues Array zurückzugeben. {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
Das Zählen der Anzahl von Wahrheiten in einem Booleschen Array ist jedoch schwieriger, da sich der akkumulierte Typ (int) vom Quelltyp (bool) unterscheidet. hier ist ein Samen notwendig, um die zweite Überladung zu nutzen.
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]
wird es[3,3,4]
dann[6,4]
und endlich sein[10]
. Anstatt ein Array mit einem einzelnen Wert zurückzugeben, erhalten Sie nur den Wert selbst.