Cela peut sembler boiteux, mais je n'ai pas pu trouver une très bonne explication Aggregate
.
Bon signifie court, descriptif, complet avec un petit exemple clair.
Cela peut sembler boiteux, mais je n'ai pas pu trouver une très bonne explication Aggregate
.
Bon signifie court, descriptif, complet avec un petit exemple clair.
Réponses:
La définition la plus simple à comprendre de Aggregate
est qu'il effectue une opération sur chaque élément de la liste en tenant compte des opérations précédentes. C'est-à-dire qu'il exécute l'action sur le premier et le deuxième élément et fait avancer le résultat. Ensuite, il opère sur le résultat précédent et le troisième élément et continue. etc.
Exemple 1. Additionner des nombres
var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // output: 10 (1+2+3+4)
Cela ajoute 1
et 2
à faire 3
. Ajoute ensuite 3
(résultat du précédent) et 3
(élément suivant dans la séquence) à faire 6
. Ajoute ensuite 6
et 4
à faire 10
.
Exemple 2. créer un csv à partir d'un tableau de chaînes
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
Cela fonctionne de la même manière. Concaténer a
une virgule et b
faire a,b
. Concatène ensuite a,b
avec une virgule et c
à faire a,b,c
. etc.
Exemple 3. Multiplication de nombres à l'aide d'une graine
Pour être complet, il y a une surcharge de ce Aggregate
qui prend une valeur de départ.
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)
Tout comme les exemples ci-dessus, cela commence par une valeur de 5
et la multiplie par le premier élément de la séquence 10
donnant un résultat de 50
. Ce résultat est reporté et multiplié par le nombre suivant de la séquence 20
pour donner un résultat de 1000
. Cela continue à travers les 2 éléments restants de la séquence.
Exemples en direct: http://rextester.com/ZXZ64749
Docs: http://msdn.microsoft.com/en-us/library/bb548651.aspx
Addenda
L'exemple 2 ci-dessus utilise la concaténation de chaînes pour créer une liste de valeurs séparées par une virgule. Il s'agit d'une manière simpliste d'expliquer l'utilisation de Aggregate
laquelle était l'intention de cette réponse. Cependant, si vous utilisez cette technique pour créer réellement une grande quantité de données séparées par des virgules, il serait plus approprié d'utiliser un StringBuilder
, et cela est entièrement compatible avec l' Aggregate
utilisation de la surcharge prédéfinie pour lancer le 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);
Exemple mis à jour: http://rextester.com/YZCVXV6464
TakeWhile
puis un Aggregate
- c'est le devoir des extensions Enumerable - ils sont facilement chaînables. Vous vous retrouvez donc avec TakeWhile(a => a == 'a').Aggregate(....)
. Voir cet exemple: rextester.com/WPRA60543
var csv = string.Join(",", chars)
(pas besoin d'agrégat ou de constructeurs de chaînes) - mais oui, je sais que l'objectif de la réponse était de donner un exemple d'utilisation d'agrégat, donc c'est cool. Mais je voulais quand même mentionner qu'il n'est pas recommandé de simplement joindre des chaînes, il y a déjà une méthode dédiée à cela ....
var biggestAccount = Accounts.Aggregate((a1, a2) => a1.Amount >= a2.Amount ? a1 : a2);
Cela dépend en partie de la surcharge dont vous parlez, mais l'idée de base est:
(currentValue, sequenceValue)
en(nextValue)
currentValue = nextValue
currentValue
Vous pouvez trouver le Aggregate
message dans ma série Edulinq utile - il comprend une description plus détaillée (y compris les différentes surcharges) et les implémentations.
Un exemple simple utilise Aggregate
comme alternative à 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);
Ou peut-être en additionnant toutes les longueurs de chaînes dans une séquence de chaînes:
int total = sequence.Aggregate(0, (current, item) => current + item.Length);
Personnellement, je trouve rarementAggregate
utile - les méthodes d'agrégation "sur mesure" sont généralement assez bonnes pour moi.
L' agrégat super court fonctionne comme le pli en Haskell / ML / F #.
Légèrement plus long .Max (), .Min (), .Sum (), .Average () tous les itérations sur les éléments dans une séquence et les agrège en utilisant la fonction d'agrégation respective. .Aggregate () est un agrégateur généralisé en ce qu'il permet au développeur de spécifier l'état de départ (aka seed) et la fonction d'agrégation.
Je sais que vous avez demandé une brève explication, mais je me suis dit que d'autres ont donné quelques brèves réponses, je me suis dit que vous seriez peut-être intéressé par une réponse un peu plus longue
Version longue avec code Une façon d'illustrer ce que cela pourrait être de montrer comment vous implémentez l' exemple d'écart-type une fois en utilisant foreach et une fois en utilisant .Aggregate. Remarque: Je n'ai pas priorisé les performances ici, donc je répète inutilement plusieurs fois la collection
D'abord une fonction d'aide utilisée pour créer une somme de distances quadratiques:
static double SumOfQuadraticDistance (double average, int value, double state)
{
var diff = (value - average);
return state + diff * diff;
}
Échantillonnez ensuite l'écart type à l'aide de 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));
}
Puis une fois en utilisant .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));
}
Notez que ces fonctions sont identiques, sauf pour la façon dont sumOfQuadraticDistance est calculé:
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
Contre:
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
Ce que fait .Aggregate, c'est qu'il encapsule ce modèle d'agrégateur et je m'attends à ce que l'implémentation de .Aggregate ressemble à ceci:
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;
}
L'utilisation des fonctions d'écart standard ressemblerait à ceci:
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);
A mon humble avis
Est-ce que .Aggregate aide à la lisibilité? En général, j'aime LINQ parce que je pense que .Where, .Select, .OrderBy et ainsi de suite aident grandement la lisibilité (si vous évitez les .Selects hiérarchiques en ligne). L'agrégat doit être dans Linq pour des raisons d'exhaustivité, mais personnellement, je ne suis pas convaincu que l'agrégat ajoute de la lisibilité par rapport à un foreach bien écrit.
SampleStandardDeviation_Aggregate()
et SampleStandardDeviation_ForEach()
ne peuvent pas l'être private
(par défaut en l'absence d'un qualificatif d'accès), elles auraient donc dû être accumulées par public
ou internal
, il me semble
Rappel:
Func<X, Y, R>
est une fonction avec deux entrées de typeX
etY
, qui renvoie un résultat de typeR
.
Enumerable.Aggregate a trois surcharges:
Surcharge 1:
A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)
Exemple:
new[]{1,2,3,4}.Aggregate((x, y) => x + y); // 10
Cette surcharge est simple, mais elle présente les limitations suivantes:
InvalidOperationException
.Surcharge 2:
B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)
Exemple:
var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n); // 2
Cette surcharge est plus générale:
bIn
).Surcharge 3:
C Aggregate<A,B,C>(IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)
La troisième surcharge n'est pas très utile IMO.
La même chose peut être écrite de manière plus succincte en utilisant la surcharge 2 suivie d'une fonction qui transforme son résultat.
Les illustrations sont adaptées de cet excellent article de blog .
Aggegate
.net qui prend un Func<T, T, T>
.
seed
, applique la fonction d'accumulateur N -1 fois; tandis que les autres (qui les surcharges ne prennent a seed
) appliquent la fonction de l' accumulateur N fois.
L'agrégat est essentiellement utilisé pour regrouper ou résumer les données.
Selon MSDN "Aggregate Function Applique une fonction d'accumulateur sur une séquence."
Exemple 1: ajoutez tous les nombres dans un tableau.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);
* important: la valeur agrégée initiale par défaut est l'élément 1 dans la séquence de collecte. c'est-à-dire que la valeur initiale totale de la variable sera 1 par défaut.
explication variable
total: il contiendra la valeur de synthèse (valeur agrégée) renvoyée par le func.
nextValue: c'est la valeur suivante dans la séquence du tableau. Cette valeur est ensuite ajoutée à la valeur agrégée, c'est-à-dire au total.
Exemple 2: ajoutez tous les éléments d'un tableau. Définissez également la valeur initiale de l'accumulateur pour commencer à ajouter à partir de 10.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
explication des arguments:
le premier argument est l'initiale (valeur de départ, c'est-à-dire la valeur de départ) qui sera utilisée pour commencer l'addition avec la valeur suivante dans le tableau.
le deuxième argument est un func qui est un func qui prend 2 int.
1. total: cela sera le même qu'avant la valeur de résumé (valeur agrégée) renvoyée par la fonction après le calcul.
2.nextValue:: il s'agit de la valeur suivante dans la séquence du tableau. Cette valeur est ensuite ajoutée à la valeur agrégée, c'est-à-dire au total.
Le débogage de ce code vous permettra également de mieux comprendre le fonctionnement des agrégats.
J'ai beaucoup appris de la réponse de Jamiec .
Si le seul besoin est de générer une chaîne CSV, vous pouvez essayer ceci.
var csv3 = string.Join(",",chars);
Voici un test avec 1 million de cordes
0.28 seconds = Aggregate w/ String Builder
0.30 seconds = String.Join
Le code source est ici
En plus de toutes les bonnes réponses ici déjà, je l'ai également utilisé pour parcourir un élément à travers une série d'étapes de transformation.
Si une transformation est implémentée en tant que Func<T,T>
, vous pouvez ajouter plusieurs transformations à a List<Func<T,T>>
et utiliser Aggregate
pour parcourir une instance de T
chaque étape.
Vous voulez prendre une string
valeur et la parcourir à travers une série de transformations de texte qui pourraient être construites par programme.
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);
Cela créera une chaîne de transformations: Supprimer les espaces de début et de fin -> supprimer le premier caractère -> supprimer le dernier caractère -> convertir en majuscules. Les étapes de cette chaîne peuvent être ajoutées, supprimées ou réorganisées selon les besoins, pour créer le type de pipeline de transformation requis.
Le résultat final de ce pipeline spécifique, c'est qu'il " cat "
devient "A"
.
Cela peut devenir très puissant une fois que vous réalisez que cela T
peut être n'importe quoi . Cela pourrait être utilisé pour les transformations d'images, comme les filtres, en utilisant BitMap
comme exemple;
Définition
La méthode d'agrégation est une méthode d'extension pour les collections génériques. La méthode d'agrégation applique une fonction à chaque élément d'une collection. Non seulement applique une fonction, mais prend son résultat comme valeur initiale pour la prochaine itération. Ainsi, en conséquence, nous obtiendrons une valeur calculée (min, max, moyenne ou autre valeur statistique) à partir d'une collection.
Par conséquent, la méthode Aggregate est une forme d'implémentation sûre d'une fonction récursive.
Sûr , car la récursion va itérer sur chaque élément d'une collection et nous ne pouvons pas obtenir de suspension de boucle infinie par une condition de sortie incorrecte. Récursif , car le résultat de la fonction actuelle est utilisé comme paramètre pour le prochain appel de fonction.
Syntaxe:
collection.Aggregate(seed, func, resultSelector);
Comment ça fonctionne:
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
Utilisation pratique:
int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);
qui fait la même chose que cette fonction:
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
Il s'agit d'une explication sur l'utilisation Aggregate
sur une API Fluent telle que 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);
et voyons que nous voulons implémenter une fonction de tri qui prend un ensemble de champs, c'est très facile à utiliser Aggregate
au lieu d'une boucle for, comme ceci:
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));
}
Et nous pouvons l'utiliser comme ceci:
var sorted = list.MySort(
s => s.LastName,
s => s.FirstName,
s => s.Age,
s => s.Grading,
s => s.TotalCourses);
Tout le monde a donné son explication. Mon explication est comme ça.
La méthode d'agrégation applique une fonction à chaque élément d'une collection. Par exemple, ayons la collection {6, 2, 8, 3} et la fonction Add (opérateur +) qu'elle fait (((6 + 2) +8) +3) et renvoie 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
Dans cet exemple, la méthode nommée Add est passée au lieu de l'expression lambda.
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; }
Une définition courte et essentielle pourrait être celle-ci: la méthode d'extension Linq Aggregate permet de déclarer une sorte de fonction récursive appliquée sur les éléments d'une liste, dont les opérandes sont deux: les éléments dans l'ordre dans lequel ils sont présents dans la liste, un élément à la fois, et le résultat de l'itération récursive précédente ou rien si ce n'est pas encore la récursivité.
De cette façon, vous pouvez calculer la factorielle des nombres ou concaténer des chaînes.
Agrégat utilisé pour additionner des colonnes dans un tableau entier multidimensionnel
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 est utilisé dans la fonction Aggregate pour additionner les colonnes correspondantes et renvoyer un nouveau tableau; {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
Mais compter le nombre de vrais dans un tableau booléen est plus difficile car le type accumulé (int) diffère du type source (bool); ici une graine est nécessaire pour utiliser la deuxième surcharge.
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]
sera-t-il[3,3,4]
alors[6,4]
et enfin[10]
. Mais au lieu de renvoyer un tableau d'une seule valeur, vous obtenez simplement la valeur elle-même.