Tietojenkäsittelytieteessä ohjelmointikielellä on ensiluokkaisia funktioita , jos se käsittelee toimintoja ensimmäisen luokan objekteina . Tämä tarkoittaa erityisesti sitä, että kieli tukee funktioiden välittämistä argumenteiksi muille funktioille, niiden palauttamista muiden funktioiden tuloksena, niiden määrittämistä muuttujiin tai niiden tallentamista tietorakenteisiin [1] . Jotkut ohjelmointikielen teoreetikot pitävät tarpeellisena tukea myös anonyymejä toimintoja [2] . Kielessä, jossa on ensiluokkaisia funktioita, funktioiden nimillä ei ole erityisasemaa, niitä käsitellään normaaleina arvoina, joiden tyyppi on funktio [3] . Termiä käytti ensimmäisenä Christopher Strachey "toimii ensiluokkaisina esineinä" yhteydessä 1960-luvun puolivälissä [4] .
Ensiluokkaiset funktiot ovat olennainen osa toiminnallista ohjelmointia , jossa korkeamman asteen funktioiden käyttö on vakiokäytäntö. Yksinkertainen esimerkki korkeamman asteen funktiosta on Map -funktio , joka ottaa funktion ja listan argumentteinaan ja palauttaa luettelon käytettyään funktiota luettelon jokaiseen elementtiin. Jotta ohjelmointikieli tukee karttaa , sen on tuettava ohitusfunktioita argumenttina.
Välittävien funktioiden toteuttamisessa argumentteina ja niiden palauttamisessa tuloksina on joitain vaikeuksia, erityisesti sisäkkäisiin ja anonyymeihin funktioihin lisättyjen ei-paikallisten muuttujien läsnä ollessa . Historiallisesti niitä on kutsuttu funarg-ongelmiksi englanninkielisestä "funktion argumentista" [5] . Varhaiset pakolliset ohjelmointikielet selvisivät näistä ongelmista luopumalla tuen palauttamista funktioista seurauksena tai jättämällä pois sisäkkäiset funktiot ja siten ei-paikalliset muuttujat (erityisesti C ). Lisp , yksi ensimmäisistä toiminnallisista ohjelmointikielistä, käyttää dynaamista scoping -lähestymistapaa , jossa ei-paikalliset muuttujat palauttavat näiden muuttujien lähimmän määritelmän kohtaan, jossa funktiota kutsuttiin, sen sijaan, missä se ilmoitettiin. Täysi tuki ensimmäisen asteen funktioiden leksikaaliselle kontekstille otettiin käyttöön Schemessä , ja siihen sisältyy funktioviittausten käsitteleminen sulkemisina pelkkien sijasta [4] , mikä puolestaan edellyttää roskien keräämisen käyttöä .
Tässä osiossa tarkastellaan, kuinka tietyt ohjelmointiidioomit toteutetaan toiminnallisissa kielissä ensimmäisen kertaluvun funktioilla ( Haskell ) verrattuna pakollisiin kieliin, joissa funktiot ovat toisen kertaluvun objekteja ( C ).
Kielessä, jossa funktiot ovat ensimmäisen kertaluvun objekteja, funktioita voidaan välittää argumenteina muille funktioille aivan kuten mikä tahansa muu arvo. Joten esimerkiksi Haskellissa :
kartta :: ( a -> b ) -> [ a ] -> [ b ] kartta f [] = [] kartta f ( x : xs ) = f x : kartta f xsKielet, joissa funktiot eivät ole ensimmäisen asteen objekteja, mahdollistavat korkeamman asteen funktioiden toteuttamisen delegaattien avulla .
C -kielen osoittimet mahdollistavat tietyin rajoituksin korkeamman asteen funktioiden rakentamisen (voit välittää ja palauttaa nimetyn funktion osoitteen, mutta et voi luoda uusia toimintoja):
void map ( int ( * f )( int ), int x [], size_t n ) { for ( int i = 0 ; i < n ; i ++ ) x [ i ] = f ( x [ i ]); }Anonyymejä toimintoja tukevissa kielissä voit välittää tällaisen funktion argumenttina korkeamman asteen funktiolle:
pää = kartta ( \ x -> 3 * x + 1 ) [ 1 , 2 , 3 , 4 , 5 ]Kielellä, joka ei tue anonyymejä toimintoja, sinun on ensin sidottava -funktio nimeen:
int f ( int x ) { paluu 3 * x + 1 ; } int main () { intl [] = { 1 , 2 , 3 , 4 , 5 } ; kartta ( f , l , 5 ); }Jos ohjelmointikieli tukee anonyymejä tai sisäkkäisiä toimintoja, on riittävän loogista olettaa, että ne viittaavat muuttujiin funktion rungon ulkopuolella:
pää = olkoon a = 3 b = 1 kartassa ( \ x - > a * x + b ) [ 1 , 2 , 3 , 4 , 5 ]Kun funktiot esitetään puhtaassa muodossa, herää kysymys, kuinka arvot välitetään funktion rungon ulkopuolelle. Tässä tapauksessa suljin on rakennettava manuaalisesti, eikä tässä vaiheessa tarvitse puhua ensiluokkaisista toiminnoista.
typedef struct { int ( * f )( int , int , int ); int * a ; int * b ; } sulkeminen_t ; void map ( sulkeminen_t * sulkeminen , int x [], koko_t n ) { for ( int i = 0 ; i < n ; ++ i ) x [ i ] = ( * sulkeminen -> f )( * sulkeminen -> a , * sulkeminen -> b , x [ i ]); } int f ( int a , int b , int x ) { palauttaa a * x + b ; } void main () { intl [] = { 1 , 2 , 3 , 4 , 5 } ; int a = 3 ; int b = 1 ; sulkeminen_t sulkeminen = { f , &a , & b }; kartta ( & sulkeminen , l , 5 ); }Toiminnon palauttaminen palauttaa itse asiassa sen sulkemisen. C-esimerkissä kaikki sulkemiseen sisältyvät paikalliset muuttujat menevät soveltamisalan ulkopuolelle heti, kun sulkemisen muodostava funktio palaa. Myöhemmin sulkemisen pakottaminen voi johtaa määrittelemättömään käyttäytymiseen.