CodeGym /Kurslar /C# SELF /İrəli səviyyə aggregasiya ...

İrəli səviyyə aggregasiya Aggregate ilə

C# SELF
Səviyyə , Dərs
Mövcuddur

1. Giriş

Köhnə yaxşı LINQ metodları — yeməkxanadakı yemək kimidir: "cəm", "minimum" və ya "orta" seçdin, keçdin. Amma bəzən nəsə xüsusi bir şey istəyirsən. Bax, burada Aggregate ortaya çıxır — sanki öz reseptinə görə bişirən şef kimi.

Əgər funksional proqramlaşdırma ilə müqayisə etsək, AggregateReduce (və ya fold) kimidir: kolleksiyadan keçirsən və addım-addım onu bir dəyərə "yığışdırırsan". Necə — özün qərar verirsən. Tam nəzarət, tam yaradıcılıq.

Aggregate ilə rahat həll olunan tapşırıqlar:

  • Mürəkkəb cəmlər almaq: məsələn, bütün ədədlərin hasilini, ya da yalnız cüt/tək ədədlərin cəmini, ya da xüsusi qayda ilə cəmləmək.
  • Sətirləri öz qaydanla birləşdirmək (məsələn, cüt və tək indekslər üçün fərqli ayırıcı ilə).
  • Sətir hesabatları qurmaq ("Markdown-listlər", HTML, istənilən custom format).
  • Dəyişən vəziyyətli kolleksiyalar qurmaq (məsələn, obyektlər siyahısından xüsusi açar qaydası ilə dictionary qurmaq).
  • Standart aggregat funksiyalara daxil olmayan istənilən mürəkkəb hesablamalar.

2. Aggregate metodunun signature və iş prinsipi

Gəlin Microsoft-un rəsmi Enumerable.Aggregate sənədinə baxaq:

public static TAccumulate Aggregate<TSource, TAccumulate>(
    this IEnumerable<TSource> source, 
    TAccumulate seed, 
    Func<TAccumulate, TSource, TAccumulate> func
)

Burada nə nədir:

  • source — sənin ilkin kolleksiyan.
  • seed — "başlanğıc dəyəri" (start akkumulyator kimi baxmaq olar).
  • func — iki arqument qəbul edən funksiya: toplanmış dəyər (acc), kolleksiyanın cari elementi və yeni toplanmış dəyəri qaytarır.

Bir də sadə overload var:

public static TSource Aggregate<TSource>(
    this IEnumerable<TSource> source, 
    Func<TSource, TSource, TSource> func
)

Bu variantda "başlanğıc" birinci element sayılır, sonra func ikinci elementdən sonuncuya qədər çağırılır.

3. Aggregate istifadəsinin ən sadə nümunələri

Tələbələr üçün mini-intriga: hamı bilir ki, Aggregate Sum və ya Product-ı əvəz edə bilər?

int[] numbers = { 2, 3, 4 };

// Cəmi hesablamaq
int sum = numbers.Aggregate((acc, val) => acc + val); // acc = toplanan, val = növbəti element

// Hasil hesablamaq
int product = numbers.Aggregate((acc, val) => acc * val);

Console.WriteLine(sum);     // 9
Console.WriteLine(product); // 24

Görünüşcə SumMultiply kimidir, amma özümüz edirik! Zarafat etmək olar: Sum metodu ilə həmişə cəm hesablamaq olar, amma Aggregate ilə həm cəm, həm "anti-cəm", hətta "kvadrat köklərin cəmi" də hesablamaq olar.

4. Sətirləri birləşdirmək üçün Aggregate istifadəsi

Bütün sətirləri birləşdirək, aralarına vergül qoyaq (sonda artıq vergül olmadan):

string[] words = { "C#", "LINQ", "rocks" };
string result = words.Aggregate((acc, word) => acc + ", " + word);
// nəticə: "C#, LINQ, rocks"

Əgər kolleksiya boş ola bilərsə, seed (başlanğıc dəyəri) çox rahatdır:

// Boş sətirdən başlayaq
string report = words.Aggregate(
    "Texnologiyalar: ",
    (acc, word) => acc + word + "; ",
    acc => acc.TrimEnd(' ', ';') // sonda artıq ";" silirik
);

Console.WriteLine(report); // "Texnologiyalar: C#; LINQ; rocks"

Diqqət yetir üçüncü arqumentə — nəticəni dəyişən funksiya (result selector), bu seed-lə overload-da var. Sanki "desertlə işləmə" kimi final yeməyin.

5. Aggregate tətbiq olunan proqramda

Gəlin yadımıza salaq, artıq neçə gündür əlləşdiyimiz tədris proqramımız var. Tutaq ki, Student adlı class-ımız var:

public class Student
{
    public string Name { get; set; }
    public int Grade { get; set; }
}

Tələbə siyahısı:

var students = new List<Student>
{
    new Student { Name = "Alisa", Grade = 5 },
    new Student { Name = "Bob", Grade = 4 },
    new Student { Name = "Vasya", Grade = 3 },
    new Student { Name = "Mariya", Grade = 5 }
};

Tapşırıq: belə bir sətir almaq
"Ən yaxşılar: Alisa, Mariya"
— yəni Grade == 5 olanların hamısı.

Adətən yeni başlayanlar belə edir:

var best = "";
foreach (var s in students)
{
    if (s.Grade == 5)
        best += s.Name + ", ";
}
best = best.TrimEnd(',', ' ');
Console.WriteLine("Ən yaxşılar: " + best);

İndi isə — LINQ-stilində Aggregate ilə:

var bestStr = students
    .Where(s => s.Grade == 5)
    .Select(s => s.Name)
    .Aggregate("Ən yaxşılar: ", (acc, name) => acc + name + ", ")
    .TrimEnd(',', ' ');

Console.WriteLine(bestStr);

Gözəllik: minimum kod, maksimum oxunaqlılıq. Hətta rəhbərin LINQ bilməsə də, ideyanı başa düşəcək (ya da heç olmasa səylərini qiymətləndirəcək, başa düşməsə də).

6. Daha mürəkkəb ssenarilər

Əslində, Aggregate-də akkumulyator təkcə ədəd və ya sətir yox, istənilən şey ola bilər: dictionary, öz class-ın və ya strukturun.

Məsələn: tələbə siyahısından hər qiymət üçün tələbə sayını hesablamaq:

var gradeCounts = students.Aggregate(
    new Dictionary<int, int>(),
    (dict, student) => {
        if (dict.ContainsKey(student.Grade))
            dict[student.Grade]++;
        else
            dict[student.Grade] = 1;
        return dict;
    }
);

// Çap üçün:
foreach (var pair in gradeCounts)
{
    Console.WriteLine($"Qiymət {pair.Key}: {pair.Value} tələbə");
}

Bu yanaşma əslində GroupBy-ı əl ilə edir. Niyə sadəcə GroupBy istifadə etmirik? Bəzən xüsusi aggregasiya lazımdır, standart LINQ metodlarında olmayan, məsələn, yalnız Vasya olmayan tələbələri toplamaq və ya hesabat üçün hesabat qurmaq.

7. Vizualizasiya: Aggregate necə işləyir (blok-sxem)

Tutaq ki, { 2, 4, 3 } massivimiz var və cəmi toplamaq istəyirik:


acc: 2 (birinci element)
      |
      v
val: 4
acc = acc + val = 2 + 4 = 6
      |
      v
val: 3
acc = acc + val = 6 + 3 = 9
      |
      v
[Bütün elementlər işlənib]
      |
      v
Nəticə: 9

Seed-lə overload üçün eyni sxem:


seed: 0
      |
      v
val: 2
acc = 0 + 2 = 2
      |
      v
val: 4
acc = 2 + 4 = 6
      |
      v
val: 3
acc = 6 + 3 = 9
      |
      v
Nəticə: 9

Aggregate və digər aggregat metodların müqayisəsi

Metod Standart davranış Elastiklik Boş kolleksiyalar üçün İş nümunəsi
Sum
Ədədləri cəmləyir Aşağı 0 qaytarır
[1,2,3].Sum() = 6
Count
Elementləri sayır Aşağı 0 qaytarır
[a,b].Count() = 2
Aggregate
İstənilən hesab Yüksək Seed tələb edir
[1,2,3].Aggregate((a,b)=>a*b)=6
string.Join
Sətirləri birləşdirir Orta Boş üçün = ""
string.Join(", ", arr)

8. Praktik məsləhətlər, tipik səhvlər və vacib məqamlar

Çox elastik olduğuna görə, Aggregate səhv istifadə olunanda gözlənilməz nəticələr verə bilər. Ən çox rast gəlinən səhvlər:

Bəzən proqramçılar seed-i (başlanğıc dəyəri) unudurlar və nəzərə almırlar ki, kolleksiya boş olanda seed-siz overload exception atacaq (InvalidOperationException). Ona görə də boş kolleksiyalar üçün seed-lə overload istifadə et:

var sum = new int[0].Aggregate(0, (acc, n) => acc + n); // İşləyir! 0 qaytarır

Əgər sətiri Aggregate ilə topluyursansa, artıq ayırıcı (məsələn, sonda ",") asanlıqla çıxır. Onu .TrimEnd(',', ' ') ilə silmək yaxşıdır — ya da sadəcə sətirləri birləşdirirsənsə, string.Join istifadə et.

Dəyişən tipli akkumulyator (məsələn, List və ya Dictionary) tez-tez Aggregate-də istifadə olunur, amma ehtiyatlı ol: əgər onu yerində dəyişirsənsə (in-place), hər addımda bütün referenslər eyni obyektə baxır. Bu, paralel əməliyyatlarda və ya kopyalama gözləyirsənsə qəribə effektlər verə bilər. Ona görə də təmiz funksional stildə hər addımda yeni obyekt qaytarmaq yaxşıdır, köhnəni dəyişmək yox.

Başqalarının istifadə etdiyi kodda, Aggregate-i "cool" görünmək üçün çətinləşdirmə: yeni başlayanlara o, sadə foreach və ya standart aggregatlara nisbətən çətin gəlir. Tapşırıq sadədirsə — Sum, Count, Join istifadə et. Amma "xüsusi şablonla mətn yığmaq" lazımdırsa — Aggregate ən yaxşı dostundur!

9. Real iş, müsahibələr və framework-larla əlaqə

Sənayedə Aggregate metodu əsasən qeyri-adi hesablamalar və ya data folding lazım olanda rast gəlinir, məsələn, mürəkkəb hesabatlar, statistika, qraf, hash hesablama, unikal id generasiyası və ya hətta xüsusi qayda ilə kolleksiyadan UI komponentləri qurmaq üçün.

Müsahibələrdə LINQ ilə bağlı suallar demək olar ki, həmişə olur: "Massivin elementlərinin cəmini necə tapmaq olar?", "Sətir siyahısını bir sətirə necə çevirmək olar?" və ya "Unikal elementlərin sayını necə hesablamaq olar?" — və tez-tez LINQ və o cümlədən Aggregate ilə həll gözləyirlər. Suallar kreativ də ola bilər: "LINQ ilə cüt ədədlərin kvadratlarının cəmini hesablaya və sonra bu prosesi təsvir edən sətir qaytara bilərsinizmi?"

Çox məşhur .NET kitabxana və framework-lərində, məsələn, Entity Framework, Dapper, RavenDB-də Aggregate metodu birbaşa DB tərəfində nadir hallarda istifadə olunur, amma kod səviyyəsində yaddaşda aggregasiya üçün aktiv istifadə olunur.

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Ən sadə aqreqasiya — massivdəki ədədlərin hasilini tapmaq
Ən sadə aqreqasiya — massivdəki ədədlərin hasilini tapmaq
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Verilənlərin qruplaşdırılması
İrəli səviyyə LINQ: qruplaşdırma və aqreqat funksiyalar
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION