Inden for datalogi har et programmeringssprog førsteklasses funktioner, hvis det behandler funktioner som førsteklasses objekter . Dette betyder især, at sproget understøtter at overføre funktioner som argumenter til andre funktioner, returnere dem som et resultat af andre funktioner, tildele dem til variabler eller gemme dem i datastrukturer [1] . Nogle programmeringssprogsteoretikere anser det for nødvendigt også at understøtte anonyme funktioner [2] . I sprog med førsteklasses funktioner har funktionsnavne ingen særlig status, de behandles som normale værdier, hvis type er en funktion [3] . Udtrykket blev første gang brugt af Christopher Strachey i forbindelse med "funktioner som førsteklasses objekter" i midten af 1960'erne [4] .
Førsteklasses funktioner er en integreret del af funktionel programmering , hvor brugen af højere ordens funktioner er standardpraksis. Et simpelt eksempel på en højere-ordens funktion ville være Map -funktionen , som tager en funktion og en liste som sine argumenter og returnerer listen efter at have anvendt funktionen på hvert element i listen. For at et programmeringssprog skal understøtte Map , skal det understøtte videregivelse af funktioner som et argument.
Der er nogle vanskeligheder med at implementere videregivelse af funktioner som argumenter og returnere dem som resultater, især i tilstedeværelsen af ikke-lokale variable introduceret i indlejrede og anonyme funktioner . Historisk set er de blevet kaldt funarg problems , fra det engelske "function argument" [5] . Tidlige imperative programmeringssprog kom uden om disse problemer ved at droppe støtte til returnering af funktioner som et resultat, eller ved at droppe indlejrede funktioner og dermed ikke-lokale variabler (især C ). Lisp , et af de første funktionelle programmeringssprog, har en dynamisk scoping -tilgang , hvor ikke-lokale variabler returnerer den nærmeste definition af disse variable til det punkt, hvor funktionen blev kaldt, i stedet for det punkt, hvor den blev erklæret. Fuld støtte til den leksikale kontekst af første-ordens funktioner blev introduceret i Scheme og involverer at behandle funktionsreferencer som lukninger i stedet for rene [4] , hvilket igen nødvendiggør brugen af skraldindsamling .
Dette afsnit ser på, hvordan specifikke programmeringssprog implementeres i funktionelle sprog med førsteordensfunktioner ( Haskell ) versus imperative sprog, hvor funktioner er andenordensobjekter ( C ).
På sprog, hvor funktioner er første-ordens objekter, kan funktioner overføres som argumenter til andre funktioner ligesom enhver anden værdi. Så for eksempel i Haskell :
kort :: ( a -> b ) -> [ a ] -> [ b ] kort f [] = [] kort f ( x : xs ) = f x : kort f xsSprog, hvor funktioner ikke er førsteordensobjekter, tillader , at funktioner af højere orden kan implementeres ved brug af delegerede .
Pointere på C -sproget giver dig med nogle begrænsninger mulighed for at bygge funktioner af højere orden (du kan videregive og returnere adressen på en navngivet funktion, men du kan ikke generere nye funktioner):
void map ( int ( * f )( int ), int x [], size_t n ) { for ( int i = 0 ; i < n ; i ++ ) x [ i ] = f ( x [ i ]); }På sprog, der understøtter anonyme funktioner, kan du overføre en sådan funktion som et argument til en højere-ordens funktion:
hoved = kort ( \ x -> 3 * x + 1 ) [ 1 , 2 , 3 , 4 , 5 ]På sprog, der ikke understøtter anonyme funktioner, skal du først binde -funktionen til navnet:
int f ( int x ) { returner 3 * x + 1 ; } int main () { intl [] = { 1 , 2 , 3 , 4 , 5 } ; kort ( f , l , 5 ); }Hvis et programmeringssprog understøtter anonyme eller indlejrede funktioner, er det logisk nok at antage, at de vil referere til variabler uden for funktionskroppen:
main = lad a = 3 b = 1 i kort ( \ x -> a * x + b ) [ 1 , 2 , 3 , 4 , 5 ]Når funktioner er repræsenteret i ren form, opstår spørgsmålet om, hvordan man sender værdier uden for funktionskroppen. I dette tilfælde skal du bygge lukningen manuelt, og på dette stadium er det ikke nødvendigt at tale om førsteklasses funktioner.
typedef struct { int ( * f )( int , int , int ); int * a ; int * b ; } closure_t ; void map ( closure_t * closure , int x [], size_t n ) { for ( int i = 0 ; i < n ; ++ i ) x [ i ] = ( * lukning -> f )( * lukning -> a , * lukning -> b , x [ i ]); } int f ( int a , int b , int x ) { returner a * x + b ; } void main () { intl [] = { 1 , 2 , 3 , 4 , 5 } ; int a = 3 ; int b = 1 ; closure_t closure = { f , &a , & b }; kort ( & lukning , l , 5 ); }At returnere en funktion returnerer faktisk dens lukning. I C-eksemplet vil alle lokale variabler, der er indesluttet i lukningen, gå uden for scope , så snart funktionen, der udgør lukningen, vender tilbage. At tvinge lukningen senere kan føre til udefineret adfærd.