Może zabrzmi to kiepsko, ale nie udało mi się znaleźć naprawdę dobrego wytłumaczenia Aggregate
.
Dobry oznacza krótki, opisowy, kompleksowy z małym i wyraźnym przykładem.
Może zabrzmi to kiepsko, ale nie udało mi się znaleźć naprawdę dobrego wytłumaczenia Aggregate
.
Dobry oznacza krótki, opisowy, kompleksowy z małym i wyraźnym przykładem.
Odpowiedzi:
Najłatwiejszą do zrozumienia definicją Aggregate
jest to, że wykonuje operację na każdym elemencie listy, biorąc pod uwagę operacje, które zostały wykonane wcześniej. Oznacza to, że wykonuje akcję na pierwszym i drugim elemencie i przenosi wynik do przodu. Następnie działa na poprzednim wyniku i trzecim elemencie i kontynuuje. itp.
Przykład 1. Sumowanie liczb
var nums = new[]{1,2,3,4};
var sum = nums.Aggregate( (a,b) => a + b);
Console.WriteLine(sum); // output: 10 (1+2+3+4)
To dodaje 1
i 2
robi 3
. Następnie dodaje 3
(wynik z poprzedniego) i 3
(następny element w sekwencji) do wykonania 6
. Następnie dodaje 6
i 4
do zrobienia 10
.
Przykład 2. utwórz plik csv z tablicy ciągów
var chars = new []{"a","b","c", "d"};
var csv = chars.Aggregate( (a,b) => a + ',' + b);
Console.WriteLine(csv); // Output a,b,c,d
Działa to w podobny sposób. Połącz a
przecinek i b
wykonaj a,b
. Następnie konkatenuje a,b
przecinek i c
tworzy a,b,c
. i tak dalej.
Przykład 3. Mnożenie liczb za pomocą zarodka
Dla kompletności, jest przeciążenie od Aggregate
którego przyjmuje wartość nasion.
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)
Podobnie jak w powyższych przykładach, zaczyna się od wartości 5
i mnoży ją przez pierwszy element sekwencji, 10
dając wynik 50
. Ten wynik jest przenoszony do przodu i mnożony przez następną liczbę w sekwencji, 20
aby dać wynik 1000
. Kontynuuje to przez pozostałe 2 elementy sekwencji.
Przykłady na żywo: http://rextester.com/ZXZ64749
Dokumenty: http://msdn.microsoft.com/en-us/library/bb548651.aspx
Uzupełnienie
W powyższym przykładzie 2 zastosowano konkatenację łańcuchów, aby utworzyć listę wartości oddzielonych przecinkiem. Jest to uproszczony sposób objaśnienia, którego użycie Aggregate
było celem tej odpowiedzi. Jeśli jednak użyjesz tej techniki do faktycznego utworzenia dużej liczby danych oddzielonych przecinkami, bardziej odpowiednie byłoby użycie a StringBuilder
, i jest to całkowicie zgodne z Aggregate
użyciem inicjowanego przeciążenia do zainicjowania 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);
Zaktualizowany przykład: http://rextester.com/YZCVXV6464
TakeWhile
następnie Aggregate
- który jest hitem rozszerzeń Enumerable - można je łatwo łączyć. Więc skończysz z TakeWhile(a => a == 'a').Aggregate(....)
. Zobacz ten przykład: rextester.com/WPRA60543
var csv = string.Join(",", chars)
(bez potrzeby agregacji lub konstruktorów ciągów) - ale tak, wiem, że celem odpowiedzi było podanie przykładowego użycia agregacji, aby było fajne. Ale nadal chciałem wspomnieć, że nie jest to zalecane do łączenia ciągów, istnieje już metoda przeznaczona do tego ....
var biggestAccount = Accounts.Aggregate((a1, a2) => a1.Amount >= a2.Amount ? a1 : a2);
Zależy to częściowo od tego, o którym przeciążeniu mówisz, ale podstawową ideą jest:
(currentValue, sequenceValue)
w(nextValue)
currentValue = nextValue
currentValue
Może się Aggregate
przydać post z mojej serii Edulinq - zawiera bardziej szczegółowy opis (w tym różne przeciążenia) i implementacje.
Jednym prostym przykładem jest użycie Aggregate
jako alternatywy dla 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);
A może sumując wszystkie długości ciągów w sekwencji ciągów:
int total = sequence.Aggregate(0, (current, item) => current + item.Length);
Osobiście rzadko uważam za Aggregate
użyteczne - „dostosowane” metody agregacji są zazwyczaj dla mnie wystarczające.
Super krótki agregat działa jak fold w Haskell / ML / F #.
Nieco dłuższe .Max (), .Min (), .Sum (), .Average () wszystkie iterują elementy w sekwencji i agregują je za pomocą odpowiedniej funkcji agregującej. .Aggregate () jest uogólnionym agregatorem, ponieważ umożliwia programistom określenie stanu początkowego (inaczej seed) i funkcji agregującej.
Wiem, że poprosiłeś o krótkie wyjaśnienie, ale pomyślałem, że gdy inni udzielili kilku krótkich odpowiedzi, pomyślałem, że być może zainteresuje Cię nieco dłuższa
Długa wersja z kodem Jednym ze sposobów zilustrowania, co to może być, pokazanie, w jaki sposób zaimplementowano przykładowe odchylenie standardowe przy użyciu foreach i raz .gregat. Uwaga: nie nadałem tutaj priorytetu wydajności, więc kilkakrotnie iteruję niepotrzebnie
Najpierw funkcja pomocnicza używana do utworzenia sumy kwadratowych odległości:
static double SumOfQuadraticDistance (double average, int value, double state)
{
var diff = (value - average);
return state + diff * diff;
}
Następnie przykładowe odchylenie standardowe za pomocą 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));
}
Następnie po użyciu .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));
}
Zauważ, że te funkcje są identyczne, z wyjątkiem sposobu obliczania sumOfQuadraticDistance:
var state = seed;
foreach (var value in ints)
{
state = SumOfQuadraticDistance (average, value, state);
}
var sumOfQuadraticDistance = state;
Przeciw:
var sumOfQuadraticDistance = ints
.Aggregate (
seed,
(state, value) => SumOfQuadraticDistance (average, value, state)
);
Więc .Aggregate robi to, że hermetyzuje ten wzorzec agregatora i oczekuję, że implementacja .Aggregate będzie wyglądać mniej więcej tak:
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;
}
Korzystanie z funkcji odchylenia standardowego wyglądałoby mniej więcej tak:
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);
moim zdaniem
Czy więc .Aggregate poprawia czytelność? Ogólnie uwielbiam LINQ, ponieważ myślę .Gdzie, .Select, .OrderBy i tak dalej znacznie poprawia czytelność (jeśli unikniesz wbudowanych hierarhicznych. Agregat musi być w Linq ze względów kompletności, ale osobiście nie jestem przekonany, że Agregat zwiększa czytelność w porównaniu do dobrze napisanego foreach.
SampleStandardDeviation_Aggregate()
i SampleStandardDeviation_ForEach()
nie może być private
(domyślnie w przypadku braku dostępu eliminacjach), więc powinny zostać naliczone przez jedną public
lub internal
, wydaje mi się
Przypomnienie:
Func<X, Y, R>
to funkcja z dwoma wejściami typuX
iY
, która zwraca wynik typuR
.
Enumerable.Aggregate ma trzy przeciążenia:
Przeciążenie 1:
A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)
Przykład:
new[]{1,2,3,4}.Aggregate((x, y) => x + y); // 10
To przeciążenie jest proste, ale ma następujące ograniczenia:
InvalidOperationException
.Przeciążenie 2:
B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)
Przykład:
var hayStack = new[] {"straw", "needle", "straw", "straw", "needle"};
var nNeedles = hayStack.Aggregate(0, (n, e) => e == "needle" ? n+1 : n); // 2
To przeciążenie jest bardziej ogólne:
bIn
).Przeciążenie 3:
C Aggregate<A,B,C>(IEnumerable<A> a, B bIn, Func<B,A,B> f, Func<B,C> f2)
Trzecie przeciążenie nie jest zbyt przydatne IMO.
To samo można napisać bardziej zwięźle, używając przeciążenia 2, a następnie funkcji, która przekształca jego wynik.
Ilustracje zostały zaadaptowane z tego doskonałego posta na blogu .
Aggegate
w .net, które wymaga Func<T, T, T>
.
seed
, stosuje funkcję akumulatora N -1 razy; podczas gdy inne przeciążenia (które mają potrwać seed
) zastosować funkcję akumulatora N razy.
Agregacja jest zasadniczo używana do grupowania lub sumowania danych.
Według MSDN „Funkcja agregująca Stosuje funkcję akumulatora w sekwencji”.
Przykład 1: Dodaj wszystkie liczby do tablicy.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate((total, nextValue) => total + nextValue);
* ważne: Domyślna początkowa wartość zagregowana to 1 element w sekwencji kolekcji. tj .: całkowita wartość początkowa zmiennej domyślnie będzie wynosić 1.
objaśnienie zmiennych
total: przechowuje wartość sumaryczną (wartość zagregowana) zwróconą przez func.
nextValue: jest to kolejna wartość w sekwencji tablicowej. Wartość ta jest następnie dodawana do wartości zagregowanej, tj. Sumy.
Przykład 2: Dodaj wszystkie elementy w tablicy. Ustaw także początkową wartość akumulatora, aby rozpocząć dodawanie od 10.
int[] numbers = new int[] { 1,2,3,4,5 };
int aggregatedValue = numbers.Aggregate(10, (total, nextValue) => total + nextValue);
wyjaśnienie argumentów:
pierwszy argument to wartość początkowa (wartość początkowa, tj. wartość początkowa), która zostanie użyta do rozpoczęcia dodawania z kolejną wartością w tablicy.
drugi argument to func, który wymaga funcji 2 int.
1. suma: będzie to miało miejsce tak samo, jak przed wartością sumaryczną (wartość zagregowana) zwróconą przez func po obliczeniu.
2.nextValue:: to kolejna wartość w sekwencji tablicowej. Wartość ta jest następnie dodawana do wartości zagregowanej, tj. Sumy.
Również debugowanie tego kodu pozwoli lepiej zrozumieć, jak działa agregacja.
Wiele się nauczyłem z odpowiedzi Jamca .
Jeśli potrzebujesz tylko wygenerować ciąg CSV, możesz spróbować.
var csv3 = string.Join(",",chars);
Oto test z 1 milionem ciągów
0.28 seconds = Aggregate w/ String Builder
0.30 seconds = String.Join
Kod źródłowy jest tutaj
Oprócz wszystkich świetnych odpowiedzi tutaj, użyłem go również do przejścia przedmiotu przez szereg etapów transformacji.
Jeśli transformacja zostanie zaimplementowana jako a Func<T,T>
, możesz dodać kilka transformacji do List<Func<T,T>>
i użyć, Aggregate
aby przejść instancję T
przez każdy krok.
Chcesz wziąć string
wartość i przeprowadzić szereg transformacji tekstu, które można zbudować programowo.
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);
Spowoduje to utworzenie łańcucha przekształceń: Usuń spacje początkowe i końcowe -> usuń pierwszy znak -> usuń ostatni znak -> przekonwertuj na wielkie litery. Kroki w tym łańcuchu można w razie potrzeby dodawać, usuwać lub zmieniać ich kolejność, aby utworzyć wymagany potok transformacji.
Rezultatem tego konkretnego rurociągu jest to, co się " cat "
dzieje "A"
.
To może stać się bardzo potężne, gdy zdasz sobie sprawę, że T
może to być cokolwiek . Można to wykorzystać do przekształceń obrazu, takich jak filtry, wykorzystując BitMap
jako przykład;
Definicja
Metoda agregująca jest metodą rozszerzającą dla zbiorów ogólnych. Metoda agregująca stosuje funkcję do każdego elementu kolekcji. Nie tylko stosuje funkcję, ale przyjmuje jej wynik jako wartość początkową dla następnej iteracji. W rezultacie otrzymamy obliczoną wartość (min, maks., Śr. Lub inną wartość statystyczną) ze zbioru.
Dlatego metoda agregująca jest formą bezpiecznej implementacji funkcji rekurencyjnej.
Bezpiecznie , ponieważ rekurencja będzie iterować po każdym elemencie kolekcji i nie możemy uzyskać zawieszenia w nieskończonej pętli z powodu złych warunków wyjścia. Rekurencyjne , ponieważ wynik bieżącej funkcji jest wykorzystywany jako parametr do następnego wywołania funkcji.
Składnia:
collection.Aggregate(seed, func, resultSelector);
Jak to działa:
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
Praktyczne zastosowanie:
int n = 7;
var numbers = Enumerable.Range(1, n);
var factorial = numbers.Aggregate((result, x) => result * x);
który robi to samo co ta funkcja:
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
To jest wyjaśnienie dotyczące używania Aggregate
płynnego interfejsu API, takiego jak Sortowanie Linq.
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);
i zobaczmy, że chcemy zaimplementować funkcję sortowania, która pobiera zestaw pól, jest to bardzo łatwe w użyciu Aggregate
zamiast pętli for, jak poniżej:
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));
}
I możemy użyć tego w następujący sposób:
var sorted = list.MySort(
s => s.LastName,
s => s.FirstName,
s => s.Age,
s => s.Grading,
s => s.TotalCourses);
Wszyscy udzielili wyjaśnień. Moje wyjaśnienie jest takie.
Metoda agregująca stosuje funkcję do każdego elementu kolekcji. Na przykład, miejmy kolekcję {6, 2, 8, 3} i funkcję Dodaj (operator +) robi (((6 + 2) +8) +3) i zwraca 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
W tym przykładzie przekazano nazwaną metodę Add zamiast wyrażenia 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; }
Krótka i niezbędna definicja może być następująca: metoda rozszerzenia agregacji Linqa pozwala zadeklarować rodzaj funkcji rekurencyjnej zastosowanej do elementów listy, których argumentami są dwa: elementy w kolejności, w jakiej są obecne na liście, jeden element na raz i wynik poprzedniej iteracji rekurencyjnej lub nic, jeśli jeszcze nie rekurencja.
W ten sposób możesz obliczyć silnię liczb lub połączyć łańcuchy.
Agregat służy do sumowania kolumn w wielowymiarowej tablicy liczb całkowitych
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()
);
Opcja Select with index jest używana w funkcji Aggregate do sumowania pasujących kolumn i zwracania nowej tablicy; {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
Ale policzenie liczby prawd w tablicy boolowskiej jest trudniejsze, ponieważ typ skumulowany (int) różni się od typu źródłowego (bool); tutaj ziarno jest konieczne, aby użyć drugiego przeciążenia.
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]
będzie[3,3,4]
potem[6,4]
, a wreszcie[10]
. Ale zamiast zwracać tablicę pojedynczej wartości, dostajesz samą wartość.