A számítástechnikában egy programozási nyelvnek első osztályú függvényei vannak , ha a függvényeket első osztályú objektumként kezeli . Ez konkrétan azt jelenti, hogy a nyelv támogatja a függvények argumentumként való átadását más függvényeknek, más függvények eredményeként való visszaadását, változókhoz rendelését vagy adatstruktúrákban való tárolását [1] . Egyes programnyelvi teoretikusok szükségesnek tartják az anonim függvények támogatását is [2] . Az első osztályú függvényekkel rendelkező nyelvekben a függvényneveknek nincs különleges státusza, normál értékként kezelik, amelynek típusa függvény [3] . A kifejezést először Christopher Strachey használta az „első osztályú objektumként funkcionál” összefüggésben az 1960-as évek közepén [4] .
Az első osztályú függvények a funkcionális programozás szerves részét képezik , amelyben a magasabb rendű függvények használata általános gyakorlat. A magasabb rendű függvény egyszerű példája a Map függvény , amely egy függvényt és egy listát vesz argumentumaként, és visszaadja a listát, miután a függvényt a lista minden elemére alkalmazta. Ahhoz, hogy egy programozási nyelv támogassa a Map -et, támogatnia kell az átadási függvényeket argumentumként.
Némi nehézségek adódhatnak az átadott függvények argumentumként való megvalósítása és eredményként való visszaadása során, különösen a beágyazott és anonim függvényekben bevezetett nem lokális változók jelenlétében . Történelmileg funarg problémáknak nevezték őket , az angol "function argument" [5] szóból . A korai kötelező programozási nyelvek úgy oldották meg ezeket a problémákat, hogy emiatt megszüntették a visszatérő függvények támogatását, vagy a beágyazott függvényeket, és így a nem helyi változókat (különösen a C -t ). A Lisp , az egyik első funkcionális programozási nyelv, dinamikus hatókörű megközelítést alkalmaz , ahol a nem lokális változók a függvény hívási pontjához legközelebbi definíciót adják vissza, ahelyett, hogy deklarálták volna. Az elsőrendű függvények lexikális kontextusának teljes támogatását a Scheme vezette be, és magában foglalja a függvényhivatkozások lezárásként való kezelését a tiszta hivatkozások helyett [4] , ami viszont szükségessé teszi a szemétgyűjtés alkalmazását .
Ez a rész azt vizsgálja, hogyan valósulnak meg bizonyos programozási idiómák az elsőrendű függvényekkel rendelkező funkcionális nyelvekben ( Haskell ), szemben az imperatív nyelvekkel, ahol a függvények másodrendű objektumok ( C ).
Azokon a nyelveken, ahol a függvények elsőrendű objektumok, a függvények argumentumként adhatók át más függvényeknek, mint bármely más érték. Tehát például a Haskellben :
térkép :: ( a -> b ) -> [ a ] -> [ b ] térkép f [] = [] térkép f ( x : xs ) = f x : térkép f xsAzok a nyelvek, ahol a függvények nem elsőrendű objektumok, lehetővé teszik a magasabb rendű funkciók megvalósítását delegáltak használatával .
A C nyelv mutatói bizonyos megkötésekkel lehetővé teszik magasabb rendű függvények építését (egy elnevezett függvény címét átadhatja és visszaadhatja, de új függvényeket nem generálhat):
void map ( int ( * f )( int ), int x [], size_t n ) { for ( int i = 0 ; i < n ; i ++ ) x [ i ] = f ( x [ i ]); }Azon nyelveken, amelyek támogatják az anonim függvényeket, az ilyen függvényt argumentumként átadhatja egy magasabb rendű függvénynek:
fő = térkép ( \ x -> 3 * x + 1 ) [ 1 , 2 , 3 , 4 , 5 ]Azon nyelveken, amelyek nem támogatják az anonim függvényeket, először az függvényt kell a névhez kötni :
int f ( int x ) { return 3 * x + 1 ; } int main () { intl [] = { 1 , 2 , 3 , 4 , 5 } ; térkép ( f , l , 5 ); }Ha egy programozási nyelv támogatja a névtelen vagy beágyazott függvényeket, akkor elég logikus azt feltételezni, hogy ezek a függvénytörzsön kívüli változókra hivatkoznak:
fő = legyen a = 3 b = 1 a térképen ( \ x -> a * x + b ) [ 1 , 2 , 3 , 4 , 5 ]Amikor a függvényeket tiszta formában ábrázoljuk, felmerül a kérdés, hogyan lehet értékeket átadni a függvénytörzsön kívülre. Ebben az esetben manuálisan kell megépíteni a zárat, és ebben a szakaszban nem kell első osztályú funkciókról beszélni.
typedef struct { int ( * f )( int , int , int ); int * a ; int * b ; } záró_t ; void térkép ( záró_t * lezárás , int x [], méret_t n ) { for ( int i = 0 ; i < n ; ++ i ) x [ i ] = ( * bezárás -> f )( * bezárás -> a , * bezárás -> b , x [ i ]); } int f ( int a , int b , int x ) { return a * x + b ; } void main () { intl [] = { 1 , 2 , 3 , 4 , 5 } ; int a = 3 ; int b = 1 ; closure_t closure = { f , &a , & b }; térkép ( & bezárás , l , 5 ); }Egy függvény visszaadása valójában annak lezárását adja vissza. A C példában a lezárásba zárt összes helyi változó kikerül a hatókörből , amint a lezárást alkotó függvény visszatér. A bezárás későbbi kényszerítése meghatározatlan viselkedéshez vezethet.