Förstklassiga funktioner

Inom datavetenskap har ett programmeringsspråk förstklassiga funktioner om det behandlar funktioner som förstaklassobjekt . I synnerhet betyder detta att språket stöder att skicka funktioner som argument till andra funktioner, returnera dem som ett resultat av andra funktioner, tilldela dem till variabler eller lagra dem i datastrukturer [1] . Vissa programmeringsspråksteoretiker anser det nödvändigt att även stödja anonyma funktioner [2] . I språk med förstklassiga funktioner har funktionsnamn ingen speciell status, de behandlas som normala värden vars typ är en funktion [3] . Termen användes först av Christopher Strachey i sammanhanget "fungerar som förstklassiga objekt" i mitten av 1960-talet [4] .

Förstaklassfunktioner är en integrerad del av funktionell programmering , där användningen av högre ordningsfunktioner är standardpraxis. Ett enkelt exempel på en högre ordningsfunktion skulle vara Map -funktionen , som tar en funktion och en lista som sina argument, och returnerar listan efter att ha tillämpat funktionen på varje element i listan. För att ett programmeringsspråk ska stödja Map måste det stödja skickande funktioner som ett argument.

Det finns vissa svårigheter med att implementera att skicka funktioner som argument och returnera dem som resultat, särskilt i närvaro av icke-lokala variabler som introduceras i kapslade och anonyma funktioner . Historiskt har de kallats funarg problems , från engelskan "function argument" [5] . Tidiga imperativa programmeringsspråk kom runt dessa problem genom att ta bort stödet för att returnera funktioner som ett resultat, eller genom att ta bort kapslade funktioner och därmed icke-lokala variabler (särskilt C ). Lisp , ett av de första funktionella programmeringsspråken, använder en dynamisk omfattningsmetod där icke-lokala variabler returnerar den närmaste definitionen av dessa variabler till den punkt där funktionen anropades, istället för den punkt där den deklarerades. Fullständigt stöd för den lexikaliska kontexten av första ordningens funktioner introducerades i Scheme och innebär att funktionsreferenser behandlas som stängningar istället för rena [4] , vilket i sin tur nödvändiggör användningen av sophämtning .

Begrepp

Det här avsnittet tittar på hur specifika programmeringsspråk implementeras i funktionella språk med första ordningens funktioner ( Haskell ) kontra imperativa språk där funktioner är andra ordningens objekt ( C ).

Funktioner av högre ordning: Skicka en funktion som ett argument

I språk där funktioner är första ordningens objekt kan funktioner skickas som argument till andra funktioner precis som alla andra värden. Så, till exempel, i Haskell :

karta :: ( a -> b ) -> [ a ] ​​-> [ b ] karta f [] = [] karta f ( x : xs ) = f x : karta f xs

Språk där funktioner inte är första ordningens objekt tillåter att funktioner av högre ordning implementeras med hjälp av delegater .

PekareC -språket, med vissa begränsningar, låter dig bygga högre ordningsfunktioner (du kan skicka och returnera adressen till en namngiven funktion, men du kan inte generera nya funktioner):

void map ( int ( * f )( int ), int x [], size_t n ) { för ( int i = 0 ; i < n ; i ++ ) x [ i ] = f ( x [ i ]); }

Anonyma och kapslade funktioner

På språk som stöder anonyma funktioner kan du skicka en sådan funktion som ett argument till en högre ordningsfunktion:

huvud = karta ( \ x -> 3 * x + 1 ) [ 1 , 2 , 3 , 4 , 5 ]

På språk som inte stöder anonyma funktioner måste du först binda -funktionen till namnet:

int f ( int x ) { returnera 3 * x + 1 ; } int main () { intl [] = { 1 , 2 , 3 , 4 , 5 } ; karta ( f , l , 5 ); }

Icke-lokala variabler och stängningar

Om ett programmeringsspråk stöder anonyma eller kapslade funktioner är det logiskt nog att anta att de kommer att referera till variabler utanför funktionskroppen:

main = låt a = 3 b = 1 i kartan ( \ x -> a * x + b ) [ 1 , 2 , 3 , 4 , 5 ]

När funktioner representeras i ren form uppstår frågan om hur man skickar värden utanför funktionskroppen. I det här fallet måste du bygga stängningen manuellt, och i detta skede är det inte nödvändigt att prata om förstklassiga funktioner.

typedef struct { int ( * f )( int , int , int ); int * a ; int * b ; } closure_t ; void map ( closure_t * closure , int x [], size_t n ) { för ( int i = 0 ; i < n ; ++ i ) x [ i ] = ( * stängning -> f )( * stängning -> a , * stängning -> b , x [ i ]); } int f ( int a , int b , int x ) { returnera a * x + b ; } void main () { intl [] = { 1 , 2 , 3 , 4 , 5 } ; int a = 3 ; int b = 1 ; closure_t closure = { f , &a , & b }; karta ( & stängning , l , 5 ); }

Funktioner med högre ordning: Returnerar funktioner som resultat

Att returnera en funktion returnerar faktiskt dess stängning. I C-exemplet kommer alla lokala variabler som ingår i stängningen att gå utanför räckvidden så snart funktionen som utgör stängningen returnerar. Att tvinga fram stängningen senare kan leda till odefinierat beteende.

Se även

Anteckningar

  1. Abelson, Harold ; Sussman, Gerald Jay Struktur och tolkning av datorprogram  (engelska) . - MIT Press , 1984. - P. Avsnitt 1.3 Formulering av abstraktioner med högre ordningsprocedurer . - ISBN 0-262-01077-1 .
  2. Programmeringsspråk pragmatik , av Michael Lee Scott, avsnitt 11.2 "Funktionell programmering".
  3. Roberto Ierusalimschy; Luiz Henrique de Figueiredo; Waldemar Celes. Implementeringen av Lua 5.0  (neopr.) . Arkiverad från originalet den 23 juni 2017.
  4. 1 2 Rod Burstall, "Christopher Strachey—Understanding Programming Languages", Higher-Order and Symbolic Computation 13:52 ( 2000)
  5. Joel Moses . "Funktionen av FUNCTION i LISP, eller varför FUNARG-problemet bör kallas miljöproblemet" Arkiverad 3 april 2015 på Wayback Machine . MIT AI Memo 199, 1970.

Litteratur

Länkar