Bevezetés az F funkcionális programozási fogalmaiba#
A funkcionális programozás a programozás olyan stílusa, amely a függvények és a nem módosítható adatok használatát hangsúlyozza. A gépelt funkcionális programozás akkor működik, ha a funkcionális programozás statikus típusokkal, például F#-tal van kombinálva. A funkcionális programozásban általában a következő fogalmak vannak kiemelve:
- Függvények elsődleges szerkezetként
- Utasítások helyett kifejezések
- Nem módosítható értékek változókon
- Deklaratív programozás imperatív programozáson keresztül
Ebben a sorozatban az F# használatával megismerheti a funkcionális programozás fogalmait és mintáit. Az út során néhány F#-t is megtanulhat.
Terminológia
A funkcionális programozás, mint más programozási paradigmák, olyan szókészlettel rendelkezik, amelyet végül meg kell tanulnia. Íme néhány gyakori kifejezés, amelyeket mindig látni fog:
- Függvény – A függvény olyan szerkezet, amely kimenetet hoz létre bemenet esetén. Formálisabban leképez egy elemet az egyik halmazból a másikba. Ezt a formalizmust számos módon emelik a betonba, különösen akkor, ha olyan függvényeket használnak, amelyek adatgyűjteményeken működnek. Ez a funkcionális programozás legalapvetőbb (és legfontosabb) fogalma.
- Kifejezés – A kifejezés olyan kódszerkezet, amely értéket hoz létre. Az F#-ban ezt az értéket kötöttnek vagy explicit módon figyelmen kívül kell hagyni. Egy kifejezés triviálisan helyettesíthető függvényhívással.
- Tisztaság – A tisztaság egy függvény olyan tulajdonsága, hogy a visszatérési értéke mindig ugyanaz ugyanazon argumentumok esetében, és a kiértékelése nem jár mellékhatások nélkül. A tiszta függvények teljes mértékben az argumentumaitól függnek.
- Hivatkozási átláthatóság – A hivatkozási átlátszóság olyan kifejezések tulajdonsága, amelyek a program viselkedésének befolyásolása nélkül helyettesíthetők a kimenetükkel.
- Nem módosítható – A megváltoztathatatlanság azt jelenti, hogy egy érték nem módosítható helyben. Ez ellentétben áll a változókkal, amelyek a helyén változhatnak.
Példák
Az alábbi példák ezeket az alapvető fogalmakat mutatják be.
Functions
A funkcionális programozás leggyakoribb és legalapvetőbb felépítése a függvény. Íme egy egyszerű függvény, amely 1-et ad hozzá egy egész számhoz:
let addOne x = x + 1
A típusaadék a következő:
val addOne: x:int -> int
Az aláírás olvasható a következőként: "addOne
elfogad egy int
elnevezettet x
, és létrehoz egy "-t int
. Formálisabban addOne
az egész számok halmazából az egész számok halmaza és az egész számok halmaza közötti érték megfeleltetése . A ->
jogkivonat ezt a leképezést jelöli. Az F#-ban általában a függvény-aláírást tekintheti meg, hogy képet kapjon arról, hogy mit csinál.
Miért fontos az aláírás? A gépelt funkcionális programozásban a függvény megvalósítása gyakran kevésbé fontos, mint a tényleges típusadektálás! Az a tény, hogy addOne
az 1 értéket hozzáadja egy egész számhoz, futásidőben érdekes, de egy program létrehozásakor az a tény, hogy elfogadja és visszaadja int
a függvényt, azt jelzi, hogyan fogja ténylegesen használni ezt a függvényt. Továbbá, ha helyesen használja ezt a függvényt (a típus-aláírás tekintetében), a problémák diagnosztizálása csak a addOne
függvény törzsén belül végezhető el. Ez a gépelt funkcionális programozás mögötti lendület.
Kifejezések
A kifejezések olyan szerkezetek, amelyek kiértékelése értékként történik. A műveletet végrehajtó állításokkal ellentétben a kifejezések arra is gondolhatnak, hogy olyan műveletet hajtanak végre, amely visszaad egy értéket. A kifejezéseket szinte mindig a funkcionális programozásban használják utasítások helyett.
Vegye figyelembe az előző függvényt. addOne
A törzs addOne
egy kifejezés:
// 'x + 1' is an expression!
let addOne x = x + 1
Ennek a kifejezésnek az eredménye határozza meg a függvény eredménytípusát addOne
. A függvényt alkotó kifejezés például egy másik típusra módosítható, például string
:
let addOne x = x.ToString() + "1"
A függvény aláírása most a következő:
val addOne: x:'a -> string
Mivel az F#-ban bármilyen típus meghívhatóToString()
, a típus x
általánossá (automatikus általánosításnak) lett nevezve, és az eredményül kapott típus egy string
.
A kifejezések nem csak a függvények testei. Olyan kifejezések is lehetnek, amelyek máshol használt értéket hoznak létre. Gyakori megoldás a következő if
:
// Checks if 'x' is odd by using the mod operator
let isOdd x = x % 2 <> 0
let addOneIfOdd input =
let result =
if isOdd input then
input + 1
else
input
result
A if
kifejezés létrehoz egy .result
Vegye figyelembe, hogy teljes egészében kihagyhatja result
a kifejezést, így a if
kifejezés a addOneIfOdd
függvény törzse lesz. A kifejezésekkel kapcsolatban fontos megjegyezni, hogy értéket hoznak létre.
Van egy speciális típus, amelyet akkor használunk, unit
ha nincs mit visszaadni. Vegyük például ezt az egyszerű függvényt:
let printString (str: string) =
printfn $"String is: {str}"
Az aláírás a következőképpen néz ki:
val printString: str:string -> unit
A unit
típus azt jelzi, hogy nincs tényleges érték visszaadva. Ez akkor hasznos, ha olyan rutinja van, amelynek "dolgoznia kell", annak ellenére, hogy a munka eredményeként nincs visszaadandó érték.
Ez éles ellentétben áll az imperatív programozással, ahol az egyenértékű if
szerkezet egy utasítás, és az értékek előállítása gyakran változók mutációjával történik. A C#-ban például a kód a következőképpen írható:
bool IsOdd(int x) => x % 2 != 0;
int AddOneIfOdd(int input)
{
var result = input;
if (IsOdd(input))
{
result = input + 1;
}
return result;
}
Érdemes megjegyezni, hogy a C# és más C stílusú nyelvek támogatják a ternáris kifejezést, amely lehetővé teszi a kifejezésalapú feltételes programozást.
A funkcionális programozásban ritkán lehet az értékeket utasításokkal mutálni. Bár egyes funkcionális nyelvek támogatják az utasításokat és a mutációt, nem gyakori, hogy ezeket a fogalmakat funkcionális programozásban használják.
Tiszta függvények
Ahogy korábban említettük, a tiszta függvények olyan függvények, amelyek:
- Mindig ugyanarra az értékre kell kiértékelni ugyanahhoz a bemenethez.
- Nincsenek mellékhatásai.
Ebben a kontextusban érdemes matematikai függvényeket is átgondolni. A matematikában a függvények csak az argumentumaiktól függnek, és nincsenek mellékhatásaik. A matematikai függvényben f(x) = x + 1
a függvény értéke f(x)
csak a függvény értékétől x
függ. A funkcionális programozásban a tiszta függvények ugyanúgy működnek.
Tiszta függvény írásakor a függvénynek csak az argumentumaitól kell függenie, és nem szabad olyan műveletet végrehajtania, amely mellékhatást eredményez.
Íme egy példa egy nem tiszta függvényre, mert globális, mutable állapottól függ:
let mutable value = 1
let addOneToValue x = x + value
A addOneToValue
függvény egyértelműen nem egyértelmű, mert value
bármikor módosítható, hogy az értéke 1-nél eltérő legyen. Ezt a globális értéktől függő mintát el kell kerülni a funkcionális programozásban.
Íme egy másik példa a nem tiszta függvényre, mert mellékhatást hajt végre:
let addOneToValue x =
printfn $"x is %d{x}"
x + 1
Bár ez a függvény nem függ globális értéktől, a program kimenetére x
írja az értéket. Bár ennek a műveletnek nincs természeténél fogva semmi baja, ez azt jelenti, hogy a függvény nem tiszta. Ha a program egy másik része a programon kívüli dologtól , például a kimeneti puffertől függ, akkor a függvény meghívása hatással lehet a program másik részére.
Az printfn
utasítás eltávolítása a függvényt tisztavá teszi:
let addOneToValue x = x + 1
Bár ez a függvény eredendően nem jobb , mint az előző verzió az printfn
utasítással, garantálja, hogy ez a függvény csak egy értéket ad vissza. A függvény meghívása tetszőleges számú alkalommal ugyanazt az eredményt eredményezi: csak egy értéket hoz létre. A tisztaság által biztosított kiszámíthatóságra sok funkcionális programozó törekszik.
Módosíthatatlanság
Végül a gépelt funkcionális programozás egyik legalapvetőbb fogalma a megváltoztathatatlanság. Az F#-ban az összes érték alapértelmezés szerint nem módosítható. Ez azt jelenti, hogy nem mutálhatók helyben, kivéve, ha ön kifejezetten mutableként jelöli meg őket.
A gyakorlatban a megváltoztathatatlan értékekkel való munka azt jelenti, hogy megváltoztatja a programozás megközelítését a "Meg kell változtatni valamit", és "új értéket kell létrehoznom".
Ha például 1-et ad hozzá egy értékhez, az azt jelenti, hogy új értéket hoz létre, nem pedig a meglévőt mutálja:
let value = 1
let secondValue = value + 1
Az F#-ban a következő kód nem mutálja a value
függvényt; ehelyett egyenlőség-ellenőrzést végez:
let value = 1
value = value + 1 // Produces a 'bool' value!
Egyes funkcionális programozási nyelvek egyáltalán nem támogatják a mutációt. Az F#-ban ez támogatott, de nem ez az értékek alapértelmezett viselkedése.
Ez a fogalom még tovább terjed az adatstruktúrákra. A funkcionális programozásban a nem módosítható adatstruktúrák, például a készletek (és még sok más) eltérő implementációval rendelkeznek, mint amire eredetileg számítani lehetett. Elméletileg egy elem készlethez való hozzáadása nem változtatja meg a készletet, hanem egy új készletet hoz létre a hozzáadott értékkel. A fedelek alatt ezt gyakran egy másik adatstruktúra teszi lehetővé, amely lehetővé teszi az értékek hatékony nyomon követését, hogy az adatok megfelelő ábrázolása eredményeképpen meg lehessen adni.
Az értékekkel és adatstruktúrákkal való munka ezen stílusa kritikus fontosságú, mivel minden olyan művelet kezelésére kényszeríti, amely módosít valamit, mintha az létrehoz egy új verziót. Ez lehetővé teszi, hogy az egyenlőség és az összehasonlíthatóság konzisztens legyen a programokban.
Következő lépések
A következő szakasz részletesen ismerteti a függvényeket, feltárva a funkcionális programozásban használható különböző módszereket.
Az F# -függvények használata mélyen feltárja a függvényeket, és bemutatja, hogyan használhatja őket különböző kontextusokban.
További olvasnivalók
A Thinking Functionally sorozat egy másik nagyszerű forrás a funkcionális programozás megismeréséhez az F#-tal. Gyakorlatias és könnyen olvasható módon ismerteti a funkcionális programozás alapjait, az F# funkcióival szemlélteti a fogalmakat.
Visszajelzés
https://aka.ms/ContentUserFeedback.
Hamarosan elérhető: 2024-ben fokozatosan kivezetjük a GitHub-problémákat a tartalom visszajelzési mechanizmusaként, és lecseréljük egy új visszajelzési rendszerre. További információ:Visszajelzés küldése és megtekintése a következőhöz: