Wyjaśnienie algorytmu agregacji LINQ


721

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:


1015

Najłatwiejszą do zrozumienia definicją Aggregatejest 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 1i 2robi 3. Następnie dodaje 3(wynik z poprzedniego) i 3(następny element w sekwencji) do wykonania 6. Następnie dodaje 6i 4do 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 aprzecinek i bwykonaj a,b. Następnie konkatenuje a,b przecinek i ctworzy a,b,c. i tak dalej.

Przykład 3. Mnożenie liczb za pomocą zarodka

Dla kompletności, jest przeciążenie od Aggregatektó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 5i mnoży ją przez pierwszy element sekwencji, 10dając wynik 50. Ten wynik jest przenoszony do przodu i mnożony przez następną liczbę w sekwencji, 20aby 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 Aggregatebył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 Aggregateuż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


11
Innym wyjaśnieniem pierwszego opisu jest to, że podana funkcja zawsze łączy pierwsze dwa elementy, dopóki tablica nie zostanie zmniejszona do jednego elementu. Więc [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ść.
David Raab,

2
Czy mogę wcześniej przerwać / wyjść z funkcji agregacji? Na przykład chars.Aggregate ((a, b) => {if (a == 'a') break the all agregate else return a + ',' + b})
Jeff Tian

13
@JeffTian - sugerowałbym utworzenie łańcucha, a TakeWhilenastę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
Jamiec

2
Jako sidenote w dodatku, cały blok można łatwo zastąpić 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 ....
T_D

2
innym powszechnym zastosowaniem (jak dotąd jedynym, jaki widziałem nawet w kodzie produkcyjnym) jest zdobycie minimalnych lub maksymalnych przedmiotów, takich jakvar biggestAccount = Accounts.Aggregate((a1, a2) => a1.Amount >= a2.Amount ? a1 : a2);
Franck

133

Zależy to częściowo od tego, o którym przeciążeniu mówisz, ale podstawową ideą jest:

  • Zacznij od zarodka jako „bieżącej wartości”
  • Iteruj po sekwencji. Dla każdej wartości w sekwencji:
    • Zastosuj funkcję użytkownika określony do przekształcenia (currentValue, sequenceValue)w(nextValue)
    • Zestaw currentValue = nextValue
  • Zwróć finał currentValue

Może się Aggregateprzydać 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 Aggregatejako 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 Aggregateużyteczne - „dostosowane” metody agregacji są zazwyczaj dla mnie wystarczające.


6
@Jon Czy istnieją asynchroniczne odmiany agregatu, które dzielą elementy na drzewo, aby umożliwić podział pracy między rdzeniami? Wygląda na to, że konstrukcja metody jest zgodna z koncepcjami „zmniejszania” lub „składania”, ale nie wiem, czy naprawdę robi to pod maską, czy po prostu iteruje po liście elementów.
AaronLS,

@Jon: wspomniany wyżej edulink nie działa, możesz przekierować mnie do odpowiedniego linku. Czy możesz sprecyzować termin „dostosowane” funkcje agregacji użyte w odpowiedzi.
Koushik

1
@Koushik: Naprawiłem link w poście. Przez „dostosowane” funkcje agregacyjne rozumiem takie rzeczy jak Max / Min / Count / Sum.
Jon Skeet

62

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.


+1 doskonale! Ale rozszerzenie metod 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ą publiclub internal, wydaje mi się
Fulproof

FYI: Jeśli dobrze pamiętam, metody rozszerzenia w mojej próbce były częścią tej samej klasy, która ich używała ==> prace prywatne w tym przypadku.
Kolejny metaprogramator

39

Obraz jest wart tysiąca słów

Przypomnienie:
Func<X, Y, R>to funkcja z dwoma wejściami typu Xi Y, która zwraca wynik typu R.

Enumerable.Aggregate ma trzy przeciążenia:


Przeciążenie 1:

A Aggregate<A>(IEnumerable<A> a, Func<A, A, A> f)

Agregat 1

Przykład:

new[]{1,2,3,4}.Aggregate((x, y) => x + y);  // 10


To przeciążenie jest proste, ale ma następujące ograniczenia:

  • sekwencja musi zawierać co najmniej jeden element, w
    przeciwnym razie funkcja wyrzuci an InvalidOperationException.
  • elementy i wynik muszą być tego samego typu.



Przeciążenie 2:

B Aggregate<A, B>(IEnumerable<A> a, B bIn, Func<B, A, B> f)

Agregat 2

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:

  • należy podać wartość początkową ( bIn).
  • kolekcja może być pusta,
    w takim przypadku funkcja zwróci wartość początkową.
  • elementy i wynik mogą mieć różne typy.



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 .


To byłaby świetna odpowiedź ... na pytanie o Haskela. Ale nie ma przeciążenia Aggegatew .net, które wymaga Func<T, T, T>.
Jamiec

4
Tak jest . Używasz go we własnej odpowiedzi!
3dGrabber 18.04.17

1
Awansowanie, ponieważ dokładnie opisujesz, co dzieje się, gdy sekwencja jest pusta. Niech N będzie liczbą elementów w źródle. Obserwujemy, że przeciążenie, które nie przyjmuje a seed, stosuje funkcję akumulatora N -1 razy; podczas gdy inne przeciążenia (które mają potrwać seed) zastosować funkcję akumulatora N razy.
Jeppe Stig Nielsen

17

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.


7

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


Kiedy uruchomiłem ten sam kod w dotnetfiddle.net, jak podano w linku, otrzymałem komunikat „Błąd krytyczny: limit użycia pamięci został przekroczony” dla „string.Join”, ale agregacja zawsze działała zgodnie z oczekiwaniami. Uważam więc, że nie jest zalecane używanie String.Join
Manish Jain

Dziwne? Kiedy skomentowałem Pierwszy stoper, który był dla Aggregate; wtedy nie pojawia się komunikat „Błąd krytyczny: limit użycia pamięci został przekroczony”. Proszę wytłumacz! Link: dotnetfiddle.net/6YyumS
Manish Jain

dotnetfiddle.net ma limit pamięci po osiągnięciu stopu wykonania. jeśli przeniesiesz kod agregujący przed kodem String.Join, możesz otrzymać błąd agregacji.
Rm558,

7

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ć, Aggregateaby przejść instancję Tprzez każdy krok.

Bardziej konkretny przykład

Chcesz wziąć stringwartość 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 Tmoże to być cokolwiek . Można to wykorzystać do przekształceń obrazu, takich jak filtry, wykorzystując BitMapjako przykład;


4

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);
  • seed - domyślna wartość początkowa;
  • func - nasza funkcja rekurencyjna. Może to być wyrażenie lambda, delegat Func lub typ funkcji TF (wynik T, T nextValue);
  • resultSelector - może to być funkcja typu func lub wyrażenie do obliczania, przekształcania, zmiany, konwersji wyniku końcowego.

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:

  1. Znajdź silnię z liczby n:

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);
}
  1. Aggregate () jest jedną z najskuteczniejszych metod rozszerzenia LINQ, takich jak Select () i Where (). Możemy go użyć do zastąpienia Sum (), Min (). Funkcja Max (), Avg () lub zmienić ją poprzez wdrożenie kontekstu dodawania:
    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);
  1. Bardziej złożone użycie metod rozszerzenia:
    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

Całkiem dobra pierwsza odpowiedź. Dobra robota! Szkoda, że ​​to takie stare pytanie, bo miałbyś dużo pozytywnych opinii
Jamiec

1

To jest wyjaśnienie dotyczące używania Aggregatepł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 Aggregatezamiast 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);

1

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; }

0

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.


0

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.