Cela peut sembler nul, mais je n'ai pas pu trouver une bonne explication de Aggregate
.
Bonne signifie courte, descriptive, complète avec un petit exemple clair.
Cela peut sembler nul, mais je n'ai pas pu trouver une bonne explication de Aggregate
.
Bonne signifie courte, descriptive, complète avec un petit exemple clair.
La définition la plus facile à comprendre de Aggregate
est qu'elle effectue une opération sur chaque élément de la liste en tenant compte des opérations précédentes. Autrement dit, elle effectue l'action sur le premier et le deuxième élément et reporte le résultat. Ensuite, elle opère sur le résultat précédent et le troisième élément et reporte le résultat. etc.
Exemple 1. Addition des nombres
var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // sortie: 10 (1+2+3+4)
Cela ajoute 1
et 2
pour faire 3
. Puis ajoute 3
(résultat précédent) et 3
(élément suivant dans la séquence) pour faire 6
. Puis ajoute 6
et 4
pour faire 10
.
Exemple 2. Créer un csv à partir d'un tableau de chaînes de caractères
var chars = new []{"a","b","c","d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Sortie a,b,c,d
Cela fonctionne de manière similaire. Concaténer a
une virgule et b
pour faire a,b
. Puis concaténer a,b
avec une virgule et c
pour faire a,b,c
. et ainsi de suite.
Exemple 3. Multiplication des nombres en utilisant une valeur de démarrage
Pour plus de complétude, il existe une surchage de Aggregate
qui prend une valeur de démarrage.
var multipliers = new []{10,20,30,40};
var multiplied = multipliers.Aggregate(5, (a,b) => a * b);
Console.WriteLine(multiplied); // Sortie 1200000 ((((5*10)*20)*30)*40)
Tout comme les exemples ci-dessus, cela commence avec 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 dans la séquence 20
pour donner un résultat de 1000
. Cela se poursuit avec 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
Addendum
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
qui é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 tout à fait compatible avec Aggregate
en utilisant la surcharge avec démarrage pour initier 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
Une autre explication de la première description est que la fonction que vous fournissez combine toujours les deux premiers membres jusqu'à ce que le tableau soit réduit à un seul élément. Ainsi, [1,2,3,4]
sera [3,3,4]
, puis [6,4]
et enfin [10]
. Mais au lieu de renvoyer un tableau d'une seule valeur, vous obtenez simplement la valeur elle-même.
Puis-je sortir/quitter prématurément d'une fonction Aggregator ? Par exemple, chars.Aggregate((a, b) => {if(a == 'a') break the whole aggregate else return a + ', ' + b})
@JeffTian - Je suggère de chaîner un TakeWhile
puis un Aggregate
- c'est là la beauté des extensions Enumerable - elles sont facilement chaînables. Ainsi, vous obtenez TakeWhile(a => a == 'a').Aggregate(....)
. Voir cet exemple : rextester.com/WPRA60543
Il dépend en partie de la surcharge à laquelle vous faites référence, mais l'idée de base est la suivante:
(valeurActuelle, valeurDeSequence)
en (valeurSuivante)
valeurActuelle = valeurSuivante
valeurActuelle
finaleVous pourriez trouver le post Aggregate
de ma série Edulinq utile - il inclut une description plus détaillée (y compris les diverses surcharges) et des implémentations.
Un exemple simple est d'utiliser Aggregate
comme alternative à Count
:
// 0 est la graine, et pour chaque élément, nous incrémentons effectivement la valeur actuelle.
// Dans ce cas, nous pouvons ignorer "élément" lui-même.
int count = sequence.Aggregate(0, (actuel, élément) => actuel + 1);
Ou peut-être additionner toutes les longueurs des chaînes dans une séquence de chaînes:
int total = sequence.Aggregate(0, (actuel, élément) => actuel + élément.Length);
Personnellement, je trouve très rarement Aggregate
utile - les méthodes d'agrégation "sur mesure" sont généralement suffisantes pour moi.
@Jon Y a-t-il des variations asynchrones d'Aggregate qui divisent les éléments en arbre afin que le travail puisse être réparti entre les cœurs? Il semble que la conception de la méthode soit cohérente avec les concepts de "réduction" ou "pliage", mais je ne sais pas si c'est vraiment ce qu'elle fait sous le capot, ou si elle itère simplement à travers la liste des éléments.
@Jon : le lien edulink mentionné ci-dessus ne fonctionne pas, pouvez-vous me rediriger vers le bon lien ? Et pouvez-vous être plus spécifique sur le terme "fonctions d'agrégation personnalisées" que vous avez utilisé dans votre réponse.
Super court Les fonctions d'agrégation fonctionnent comme fold en Haskell/ML/F#.
Légèrement plus long .Max(), .Min(), .Sum(), .Average() itère sur les éléments d'une séquence et les agrège en utilisant la fonction d'agrégation respective. .Aggregate () est un agrégateur généralisé car il permet au développeur de spécifier l'état initial (également appelé seed) et la fonction d'agrégation.
Je sais que vous avez demandé une explication courte mais comme d'autres ont donné quelques réponses courtes, j'ai pensé que vous seriez peut-être intéressé par une réponse un peu plus longue
Version longue avec du code Une façon d'illustrer ce que cela pourrait être est de montrer comment implémenter l'écart type en utilisant une boucle foreach une fois et en utilisant .Aggregate une fois. Remarque : Je n'ai pas privilégié les performances ici donc je parcours plusieurs fois la collection inutilement
Tout d'abord une fonction d'aide utilisée pour créer une somme des distances quadratiques :
static double SumOfQuadraticDistance (double average, int value, double state)
{
var diff = (value - average);
return state + diff * diff;
}
Ensuite l'écart type en utilisant ForEach :
static double SampleStandardDeviation_ForEach (
this IEnumerable 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 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));
}
Remarquez 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;
Comparé à :
var sumOfQuadraticDistance = ints
.Aggregate(
seed,
(state, value) => SumOfQuadraticDistance(average, value, state)
);
Donc ce que fait .Aggregate c'est qu'il encapsule ce motif d'agrégation et je m'attends à ce que l'implémentation de .Aggregate ressemble à ceci :
public static TAggregate Aggregate (
this IEnumerable values,
TAggregate seed,
Func aggregator
)
{
var state = seed;
foreach (var value in values)
{
state = aggregator(state, value);
}
return state;
}
Utiliser les fonctions d'écart type 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);
À mon avis
Est-ce que .Aggregate aide à la lisibilité ? En général, j'adore LINQ car je pense que .Where, .Select, .OrderBy et ainsi de suite aident grandement à la lisibilité (si vous évitez les .Selects hiérarchiques en ligne). Aggregate doit être dans Linq pour des raisons de complétude mais personnellement je ne suis pas convaincu que .Aggregate ajoute de la lisibilité par rapport à un foreach bien écrit.
+1 Excellent! Mais les méthodes d'extension SampleStandardDeviation_Aggregate()
et SampleStandardDeviation_ForEach()
ne peuvent pas être private
(par défaut en l'absence d'un qualificateur d'accès), elles auraient donc dû être déclarées soit public
soit internal
, à mon avis
Rappel :
Func
est une fonction avec deux entrées de typeX
etY
, qui renvoie un résultat de typeR
.
Enumerable.Aggregate a trois surcharges :
Surchargé 1 :
A Aggregate(this IEnumerable a, Func 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
.Surchargé 2 :
B Aggregate(this IEnumerable
a, B bIn, Func 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
) doit être fournie.Surchargé 3 :
C Aggregate(this IEnumerable
a, B bIn, Func f, Func f2)
La troisième surcharge n'est pas très utile à mon avis.
La même chose peut être écrite de manière plus concise en utilisant la surcharge 2 suivie d'une fonction qui transforme son résultat.
Les illustrations sont adaptées de ce superbe article de blog.
L'agrégat est essentiellement utilisé pour regrouper ou additionner des données.
Selon MSDN "La fonction Agrégat applique une fonction d'accumulateur sur une séquence."
Exemple 1 : Ajouter tous les nombres dans un tableau.
int[] numbers = new int[] { 1,2,3,4,5 };
int valeurAggregée = numbers.Aggregate((total, nextValue) => total + nextValue);
*important: La valeur agrégée initiale par défaut est le 1er élément dans la séquence de collection. c'est-à-dire : la valeur initiale de la variable totale sera par défaut 1.
explication des variables
totale : elle contiendra la valeur sommée (valeur agrégée) retournée par la fonction.
nextValue : il s'agit de la prochaine valeur dans la séquence du tableau. Cette valeur est ensuite ajoutée à la valeur agrégée, c'est-à-dire totale.
Exemple 2 : Ajouter tous les éléments d'un tableau. Définir également la valeur d'accumulateur initiale à partir de 10.
int[] numbers = new int[] { 1,2,3,4,5 };
int valeurAggregée = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
explication des arguments :
le premier argument est l'initial (valeur de départ, c'est-à-dire valeur initiale) qui sera utilisé pour commencer l'addition avec la prochaine valeur dans le tableau.
le deuxième argument est une fonction qui prend 2 entiers.
1.totale : ce sera le même que précédemment la valeur sommée (valeur agrégée) retournée par la fonction après le calcul.
2.nextValue : il s'agit de la prochaine valeur dans la séquence du tableau. Cette valeur est ensuite ajoutée à la valeur agrégée, c'est-à-dire totale.
De plus, déboguer ce code vous donnera une meilleure compréhension du fonctionnement de l'agrégat.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.