Questo potrebbe sembrare zoppo, ma non sono stato in grado di trovare una spiegazione davvero buona di Aggregate
.
Bene significa breve, descrittivo, completo con un piccolo esempio chiaro.
Questo potrebbe sembrare zoppo, ma non sono stato in grado di trovare una spiegazione davvero buona di Aggregate
.
Bene significa breve, descrittivo, completo con un piccolo esempio chiaro.
Risposte:
La definizione più semplice da comprendere Aggregate
è che esegue un'operazione su ciascun elemento dell'elenco tenendo conto delle operazioni precedenti. Vale a dire che esegue l'azione sul primo e sul secondo elemento e porta avanti il risultato. Quindi opera sul risultato precedente e sul terzo elemento e prosegue. eccetera.
Esempio 1. Sommare i numeri
var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // output: 10 (1+2+3+4)
Questo aggiunge 1
e 2
da fare 3
. Quindi aggiunge 3
(risultato del precedente) e 3
(elemento successivo in sequenza) da creare 6
. Quindi aggiunge 6
e 4
da fare 10
.
Esempio 2. Creare un CSV da una matrice di stringhe
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
Funziona più o meno allo stesso modo. Concatenare a
una virgola e b
di fare a,b
. Quindi concatena a,b
con una virgola e c
per creare a,b,c
. e così via.
Esempio 3. Moltiplicare i numeri usando un seme
Per completezza, c'è un sovraccarico di Aggregate
cui prende un valore seed.
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)
Proprio come negli esempi sopra, questo inizia con un valore di 5
e lo moltiplica per il primo elemento della sequenza che 10
dà un risultato di 50
. Questo risultato viene portato avanti e moltiplicato per il numero successivo nella sequenza 20
per dare un risultato di 1000
. Questo continua attraverso i restanti 2 elementi della sequenza.
Esempi live: http://rextester.com/ZXZ64749
Documenti: http://msdn.microsoft.com/en-us/library/bb548651.aspx
appendice
L'esempio 2, sopra, usa la concatenazione di stringhe per creare un elenco di valori separati da una virgola. Questo è un modo semplicistico per spiegare l'uso di Aggregate
quale era l'intenzione di questa risposta. Tuttavia, se si utilizza questa tecnica per creare effettivamente una grande quantità di dati separati da virgola, sarebbe più appropriato utilizzare a StringBuilder
, e questo è del tutto compatibile con l' Aggregate
utilizzo del sovraccarico seeded per avviare il file 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);
Esempio aggiornato: http://rextester.com/YZCVXV6464
TakeWhile
quindi un Aggregate
- questo è il dovere delle estensioni enumerabili - sono facilmente concatenabili. Quindi alla fine TakeWhile(a => a == 'a').Aggregate(....)
. Vedi questo esempio: rextester.com/WPRA60543
var csv = string.Join(",", chars)
(non c'è bisogno di aggregatori o costruttori di stringhe) - ma sì, so che il punto della risposta era di dare un esempio di utilizzo dell'aggregato, quindi è bello. Ma volevo ancora menzionare che non è raccomandato solo per unire le stringhe, c'è già un metodo dedicato per questo ....
var biggestAccount = Accounts.Aggregate((a1, a2) => a1.Amount >= a2.Amount ? a1 : a2);
Dipende in parte dal sovraccarico di cui stai parlando, ma l'idea di base è:
(currentValue, sequenceValue)
in(nextValue)
currentValue = nextValue
currentValue
Puoi trovare utile il Aggregate
post nella mia serie Edulinq - include una descrizione più dettagliata (compresi i vari sovraccarichi) e le implementazioni.
Un semplice esempio sta usando Aggregate
in alternativa a 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);
O forse sommando tutte le lunghezze delle stringhe in una sequenza di stringhe:
int total = sequence.Aggregate(0, (current, item) => current + item.Length);
Personalmente raramente trovo Aggregate
utile - i metodi di aggregazione "su misura" di solito sono abbastanza buoni per me.
L' aggregato super corto funziona come piega in Haskell / ML / F #.
Leggermente più lungo .Max (), .Min (), .Sum (), .Average () tutto scorre sugli elementi in una sequenza e li aggrega usando la rispettiva funzione di aggregazione. .Aggregate () è un aggregatore generalizzato in quanto consente allo sviluppatore di specificare lo stato iniziale (noto anche come seed) e la funzione aggregata.
So che hai chiesto una breve spiegazione, ma ho pensato che mentre gli altri davano un paio di risposte brevi, ho pensato che potresti essere interessato a uno leggermente più lungo
Versione lunga con codice Un modo per illustrare cosa potrebbe essere mostrare come implementare la Deviazione standard del campione una volta usando foreach e una volta usando .Aggregate. Nota: non ho dato priorità alle prestazioni qui, quindi ho ripetutamente ripetuto più volte la raccolta
Innanzitutto una funzione di supporto utilizzata per creare una somma di distanze quadratiche:
static double SumOfQuadraticDistance (double average, int value, double state)
{
var diff = (value - average);
return state + diff * diff;
}
Quindi campione Deviazione standard utilizzando 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));
}
Quindi una volta usando .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));
}
Si noti che queste funzioni sono identiche ad eccezione di come viene calcolato sumOfQuadraticDistance:
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
Contro:
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
Quindi, ciò che fa .Aggregate è che incapsula questo modello di aggregatore e mi aspetto che l'implementazione di .Aggregate sia simile a questa:
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'uso delle funzioni di deviazione standard sarebbe simile al seguente:
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 parer mio
Quindi .Aggregate aiuta la leggibilità? In generale, adoro LINQ perché penso .Where, .Select, .OrderBy e così via aiuta notevolmente la leggibilità (se si evita il .Select gerarchico incorporato). L'aggregato deve essere in Linq per motivi di completezza, ma personalmente non sono così convinto che. Aggregate aggiunge leggibilità rispetto a un predicatore ben scritto.
SampleStandardDeviation_Aggregate()
e SampleStandardDeviation_ForEach()
non possono essere private
(per impostazione predefinita in assenza di un qualificatore di accesso), quindi avrebbero dovuto essere maturati da uno dei due public
o internal
, mi sembra
Promemoria:
Func<X, Y, R>
è una funzione con due input di tipoX
eY
, che restituisce un risultato di tipoR
.
Enumerable.Aggregate ha tre sovraccarichi:
Sovraccarico 1:
A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)
Esempio:
new[]{1,2,3,4}.Aggregate((x, y) => x + y); // 10
Questo sovraccarico è semplice, ma presenta le seguenti limitazioni:
InvalidOperationException
.Sovraccarico 2:
B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)
Esempio:
var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n); // 2
Questo sovraccarico è più generale:
bIn
).Sovraccarico 3:
C Aggregate<A,B,C>(IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)
Il terzo sovraccarico non è molto utile IMO.
Lo stesso può essere scritto in modo più succinto usando il sovraccarico 2 seguito da una funzione che trasforma il suo risultato.
Le illustrazioni sono adattate da questo eccellente post sul blog .
Aggegate
in .net che richiede a Func<T, T, T>
.
seed
, applica la funzione accumulatore N -1 volte; mentre gli altri sovraccarichi (che non diano seed
) applicare la funzione di accumulatore N volte.
L'aggregato viene sostanzialmente utilizzato per raggruppare o sommare i dati.
Secondo MSDN "Funzione aggregata Applica una funzione accumulatore su una sequenza".
Esempio 1: aggiungere tutti i numeri in un array.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);
* importante: il valore aggregato iniziale per impostazione predefinita è l'elemento 1 nella sequenza della raccolta. vale a dire: il valore iniziale della variabile totale sarà 1 per impostazione predefinita.
spiegazione variabile
totale: conterrà il valore di somma (valore aggregato) restituito dalla funzione
nextValue: è il valore successivo nella sequenza di array. Questo valore viene quindi aggiunto al valore aggregato, cioè totale.
Esempio 2: aggiungere tutti gli elementi in un array. Impostare anche il valore iniziale dell'accumulatore per iniziare ad aggiungere con da 10.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
spiegazione degli argomenti:
il primo argomento è l'iniziale (valore iniziale, ovvero valore seme) che verrà utilizzato per iniziare l'aggiunta con il valore successivo nella matrice.
il secondo argomento è una funzione che è una funzione che richiede 2 int.
1.totale: sarà lo stesso di prima del valore di riepilogo (valore aggregato) restituito dalla funzione dopo il calcolo.
2.nextValue:: è il valore successivo nella sequenza di array. Questo valore viene quindi aggiunto al valore aggregato, cioè totale.
Anche il debug di questo codice ti darà una migliore comprensione di come funzionano gli aggregati.
Ho imparato molto dalla risposta di Jamiec .
Se l'unica necessità è generare una stringa CSV, puoi provare questo.
var csv3 = string.Join(",",chars);
Ecco un test con 1 milione di stringhe
0.28 seconds = Aggregate w/ String Builder
0.30 seconds = String.Join
Il codice sorgente è qui
Oltre a tutte le ottime risposte qui, l'ho anche usato per guidare un oggetto attraverso una serie di passaggi di trasformazione.
Se una trasformazione è implementata come a Func<T,T>
, è possibile aggiungere diverse trasformazioni a List<Func<T,T>>
e utilizzare Aggregate
per percorrere un'istanza di T
ogni passaggio.
Volete prendere un string
valore e guidarlo attraverso una serie di trasformazioni di testo che potrebbero essere costruite programmaticamente.
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);
Questo creerà una catena di trasformazioni: Rimuovi spazi iniziali e finali -> rimuovi il primo carattere -> rimuovi l'ultimo carattere -> converti in maiuscolo. I passaggi di questa catena possono essere aggiunti, rimossi o riordinati in base alle esigenze, per creare qualsiasi tipo di pipeline di trasformazione richiesta.
Il risultato finale di questa specifica pipeline è che " cat "
diventa "A"
.
Questo può diventare molto potente quando ti rendi conto che T
può essere qualsiasi cosa . Questo potrebbe essere usato per trasformazioni di immagini, come i filtri, usando BitMap
come esempio;
Definizione
Il metodo aggregato è un metodo di estensione per raccolte generiche. Il metodo aggregato applica una funzione a ciascun elemento di una raccolta. Non solo applica una funzione, ma prende il suo risultato come valore iniziale per la successiva iterazione. Di conseguenza, otterremo un valore calcolato (min, max, avg o altro valore statistico) da una raccolta.
Pertanto, il metodo aggregato è una forma di implementazione sicura di una funzione ricorsiva.
Sicuro , perché la ricorsione ripeterà ogni elemento di una raccolta e non possiamo ottenere alcuna sospensione del ciclo infinito da una condizione di uscita errata. Ricorsivo , poiché il risultato della funzione corrente viene utilizzato come parametro per la chiamata di funzione successiva.
Sintassi:
collection.Aggregate(seed, func, resultSelector);
Come funziona:
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
Uso pratico:
int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);
che sta facendo la stessa cosa di questa funzione:
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
Questa è una spiegazione sull'uso di Aggregate
un'API fluente come 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);
e vediamo che vogliamo implementare una funzione di ordinamento che accetta una serie di campi, questo è molto semplice usando al Aggregate
posto di un for-loop, come questo:
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));
}
E possiamo usarlo in questo modo:
var sorted = list.MySort(
s => s.LastName,
s => s.FirstName,
s => s.Age,
s => s.Grading,
s => s.TotalCourses);
Ognuno ha dato la sua spiegazione. La mia spiegazione è così.
Il metodo aggregato applica una funzione a ciascun elemento di una raccolta. Ad esempio, abbiamo la raccolta {6, 2, 8, 3} e la funzione Aggiungi (operatore +) fa (((6 + 2) +8) +3) e restituisce 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
In questo esempio viene passato il metodo denominato Aggiungi anziché l'espressione 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; }
Una definizione breve ed essenziale potrebbe essere questa: il metodo di estensione Linq Aggregate consente di dichiarare una sorta di funzione ricorsiva applicata agli elementi di un elenco, i cui operandi sono due: gli elementi nell'ordine in cui sono presenti nell'elenco, un elemento alla volta e il risultato della precedente iterazione ricorsiva o niente se non ancora ricorsione.
In questo modo è possibile calcolare il fattoriale dei numeri o concatenare stringhe.
Aggregato utilizzato per sommare le colonne in un array intero multidimensionale
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()
);
Seleziona con indice viene utilizzato all'interno della funzione di aggregazione per sommare le colonne corrispondenti e restituire un nuovo array; {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
Ma contare il numero di tracce in un array booleano è più difficile poiché il tipo accumulato (int) differisce dal tipo sorgente (bool); qui è necessario un seme per utilizzare il secondo sovraccarico.
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]
sarà[3,3,4]
allora[6,4]
e alla fine[10]
. Ma invece di restituire una matrice di un singolo valore ottieni semplicemente il valore stesso.