1. Giriş
Funksional proqramlaşdırma (FP) — bu proqramlaşdırmanın o cür paradigmasıdır ki, əsas tikinti bloku obyekt və ya prosedur/metod deyil, riyazi mənada funksiya-dır. FP-də əsas diqqət "nə hesablamaq"-a, nəinki "necə hesablamaq"-a verilir.
Siz artıq lambda-ifadələrlə və LINQ-lə işləyərkən FP-nin ayrı-ayrı ideyalarıyla qarşılaşmısınız. Amma fərq nədir? Praktikada: OOP obyektləri və onların qarşılıqlı təsirini təsvir edir, prosedural proqramlaşdırma — addımlar toplusu, FP isə — funksiyaların kompozisiyası, davranışı dəyər kimi ötürmək, vəziyyətin dəyişməsindən imtina (immutability) və yan effektlərin olmamasıdır.
Niyə yeni paradigma lazımdır?
- Daha təmiz, proqnozlaşdırıla bilən və test edilə bilən kod.
- Çoxiprosessli dəstəyin sadələşdirilməsi ("heç bir vəziyyət yoxdur — heç bir problem yoxdur").
- Qısa və ifadəli kod (neçə az kod — o qədər az səhv).
- Yüksək səviyyəli, asan təkrar istifadə oluna bilən abstraksiyalar.
Analojiya
Təsəvvür edin ki, restoran sifariş alıb: "omelet hazırla". İmperativ aşbaz təlimat siyahısını yerinə yetirir: yumurta götür, qır, çal, qızart. Funksional aşbaz deyir: res = omlet(yumurta) — o funksiyalarla işləyir, mətbəxin daxili vəziyyətindən abstraksiya edir (yaxşı, təxminən).
C#-da hər iki yanaşmadan istifadə edə bilərsiniz. Bu dili real layihələr üçün çox çevik və güclü edir.
FP-nin əsas anlayışları
1. Higher-order funksiyalar
Funksiyaları parametr kimi ötürmək, digər funksiyalardan qaytarmaq və dəyişənlərdə saxlamaq olar. Siz bunu lambda-ifadələrlə və delegatlarla artıq etmisiniz. FP-də belə "funksiyalar üzərində funksiyalar" hər şeyin əsasını təşkil edir.
2. Təmiz funksiyalar
Funksiya "təmiz" sayılırsa, onun nəticəsi yalnız parametrlərdən asılıdır və o özündən kənarda heç nəni dəyişmir (heç bir yan effekt yoxdur). Eyni arqumentlə iki dəfə çağırış həmişə eyni nəticəni verir.
3. Dəyişməzlik (Immutability)
Məlumat "yerində" dəyişdirilmir: yeni vəziyyət — yeni obyektdir. Bu proqram haqqında düşünməyi asanlaşdırır və çoxiprosessli proqramlarda kömək edir.
4. Yan effektlərin olmaması
Funksiya fayla yazmır, qlobal dəyişəni dəyişmir, ekrana çəkmir — sadəcə nəticəni qaytarır. Real həyatda yan effektlər qaçılmazdır, amma onları sistemin kənarlarında izolyasiya etməyə çalışırlar.
5. Funksiyaların kompozisiyası
Bir funksiyanı digərindən qurmaq olar, bloklar kimi. Məsələn: müsbət ədədləri filtrə sal, onların kvadratlarını götür və cəmlə. Hər əməliyyat ayrı funksiyadır və asanlıqla birləşir (Where → Select → Sum).
2. FP C#-da: nəzəriyyədən praktika
C# — multiparadigmalı dil: OOP-u, prosedural yanaşmanı və güclü funksional stilini (lambda-lar, delegatlar, extension metodlar və LINQ) yaxşı dəstəkləyir.
Tədris tətbiqimiz nümunəsində araşdıraq
Tutaq ki, ədədlər və sətrlər siyahısı ilə işləyən proqram üzərində işləyirik. Tapşırığımız — bu məlumatlara funksional tərzdə müxtəlif əməliyyatlar tətbiq etmək.
Nümunə 1: Higher-order funksiyaların istifadəsi
// Siyahının bütün elementlərinə action tətbiq edir
public static void ForEach<T>(List<T> items, Action<T> action)
{
foreach (var item in items)
{
action(item);
}
}
İstifadəsi:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
ForEach(numbers, n => Console.WriteLine(n * n)); // Parametr-funksiya
Görürsünüz? Funksiyanı dəyişəndə saxlamaq və ya adi dəyər kimi ötürmək olar — elə bil mətbəxdə alma verir kimi!
Nümunə 2: Təmiz funksiya
Proqramın vəziyyətini dəyişməyən və yalnız girişdən asılı funksiya:
int MultiplyByTwo(int x)
{
return x * 2;
}
- Xaricdən heç nə ilə asılı deyil.
- Xarici heç nəni dəyişmir.
- x = 5 üçün həmişə 10 qaytarır.
Müqayisə üçün funksiyaya baxın ki, qlobal dəyişəni istifadə edir və dəyişdirir:
int total = 0;
int AddToTotal(int x)
{
total += x;
return total;
}
Bu artıq təmiz funksiya deyil — nəticə xarici vəziyyətdən asılıdır və onu dəyişdirir.
Nümunə 3: Məlumatların dəyişməzliyi
Giriş məlumatlarını dəyişdirmək əvəzinə yeni nümunələr yaradırıq:
List<int> AddOneToEach(List<int> numbers)
{
return numbers.Select(n => n + 1).ToList();
}
Giriş siyahısı ümumiyyətlə dəyişmir. Çoxiprosessli proqramlarda bu xüsusilə faydalıdır: daha az lock və data race.
Nümunə 4: Funksiyaların kompozisiyası
Bütün cüt ədədlərin kvadratlarının cəmini alın:
int SumOfEvenSquares(List<int> numbers)
{
return numbers
.Where(n => n % 2 == 0) // Yalnız cütləri saxla
.Select(n => n * n) // Kvadratlaşdır
.Sum(); // Cəmlə
}
Oxunaqlı və deklarativ: hər bir əməliyyat ayrı funksiyadır.
3. Faydalı məqamlar
FP, LINQ və C#
LINQ — kolleksiyalar üçün demək olar ki "FP praktikasıdır": burada higher-order funksiyalardan istifadə edirsiniz (Where, Select və s.), yeni ardıcıllıqlar əldə edirsiniz, ilkinləri mutasiya etmədən və hər dönüşüm ayrı ifadədir. Nəticə — IEnumerable<T>, hansının ki nə almaq istədiyinizi, nəyi deyil, necə iterasiya etməyi təsvir edir.
Cədvəl analoqları
| İmperativ (prosedural/OOP) | Funksional (LINQ/FP-stil) |
|---|---|
|
|
| Kolleksiyanı "mutasiya etmək" | Yeni kolleksiya almaq |
| Vəziyyət (total += x) | Təmiz funksiyalar (xs.Sum()) |
| Necə etmək: «bunları et» | Nə istədiyimizi təsvir etmək: «nə almaq istəyirik» |
FP vs OOP: iki dünya — bir C#
Bu rəqib düşərgələr deyil. Real C# layihələrində onları qarışdırırlar: domain model-i siniflərlə (OOP) qurmaq rahatdır, kolleksiyaların işlənməsi, data aggregasiyası və transformasiyalar isə funksional stilində LINQ, lambda-lar və extension metodlar vasitəsilə aparılır.
Delegatlar haqda bilikləriniz birbaşa faydalıdır: Func<T, TResult>, Predicate<T>, Action<T> — FP-stilinin tipik tikinti bloklarıdır.
Universal filter funksiyası:
List<T> Filter<T>(List<T> items, Predicate<T> predicate)
{
var result = new List<T>();
foreach (var item in items)
{
if (predicate(item))
result.Add(item);
}
return result;
}
Çağırışlar:
var adults = Filter(people, person => person.Age >= 18);
var bigFiles = Filter(fileNames, name => name.EndsWith(".mp4") && name.Length > 10);
Fərqli şərtlərlə bir çox oxşar metod yazmaqdansa — bir universal funksiya.
İşəgötürənlər və müsahibələr üçün niyə FP tərzində inkişaf etmiş geliştiricilər?
- FP kiçik kod bloklarını bütün sistemi qaldırmadan test etməyə kömək edir.
- Loqikanı saxlamaq daha asandır: vəziyyət azdırsa, səhv mənbəyi də azdır.
- Paralel və asynchronous kod yazmaq daha asandır — qlobal vəziyyət yoxdur, data race-lər daha az olur.
Necə "fanatik olmamaq" lazımdır?
Bəli, FP güclüdür. Amma C# tamamilə funksional dil deyil və hər məsələ ideal təmizlik tələb etmir. Lazımi yerlərdə lokal dəyişənlərdən və ağlabatan mutasiyalardan qorxmayın. Əsas odur ki, oxunaqlıq, proqnozlaşdırıla bilənlik və test edilə bilmə olsun. FP elementləri — vasitədir, inanclıq deyil.
4. Yeni başlayanların tipik səhvləri
Funksional görsənən, amma əslində funksional olmayan kod üçün çox asan tələyə düşmək olar.
Məsələn, funksiya yeni kolleksiya qaytarır, amma içində yolu üzrə ilkin siyahını mutasiya edir — bu immutability prinsipini pozur və çağıran kodun gözləntilərini pozur.
Başqa misal: lambda ifadəsi xarici dəyişənə müraciət edir və onu dəyişir. Funksional paradigmada bu yan effekt sayılır və kodun davranışını daha az proqnozlaşdırıla bilən edir.
C# kompilyatoru sizi dayandırmayacaq: dil həm birini, həm də o birini etməyə imkan verir. Buna görə FP-praktikalarda diqqətli olmaq lazımdır ki, funksiya "öz-özünə yaşasın", xaricdə heç nəyi dəyişməsin və yalnız arqumentlərini oxusun.
GO TO FULL VERSION