Funktion (programmering)

Inom programmering är en funktion, även subrutin, procedur, metod, underprogram eller subprogram, en del av ett datorprogram som kan anropas för att utföra en viss uppgift oberoende av resten av koden. En funktion utformas ofta för att kunna anropas flera gånger från olika ställen i programmet och man skickar då ofta med parametrar (argument) till funktionen. En funktion innehåller ett kodblock som teoretiskt sett kan vara oändligt stort och kan i sig fungera som ett eget program (därav namnet underprogram).

En funktion kan också returnera ett värde, till exempel ett resultat av en beräkning. I vissa programspråk, till exempel Fortran och Pascal, görs en åtskillnad mellan funktioner, som returnerar ett värde, och procedurer, som inte gör det. I andra programspråk, till exempel C och Lisp, görs inte denna åtskillnad. I objektorienterad programmering kallas en funktion som är en del av ett objekt för en metod.

Funktioner är ett kraftfullt verktyg som finns i de flesta programspråk och används inom strukturerad programmering för att (bland annat):

  • Slippa skriva samma sak flera gånger
  • Göra stora program lätta att överblicka
  • Slippa göra ändringar på flera ställen i ett program
  • Låta flera programmerare arbeta med olika delar av koden samtidigt på ett enkelt sätt

Alternativet är ett program med rak kod, som inte innehåller några anrop till funktioner. Ett sådant program körs snabbare eftersom anropen (hoppen) tar tid, men å andra sidan återanvänder man inte koden vilket gör att programmet tar mer plats i datorminnet och cachen.

Grundkoncept

[redigera | redigera wikitext]

En funktions innehåll kallas dess kropp, och det är denna kod som kommer att köras när funktionen anropas (körs). Vad som sker är att programräknaren nuvarande värde sparas och räknaren sätts till den adress där funktionen börjar, varefter processorn fortsätter att utföra instruktionerna som vanligt. När funktionen är klar (en return-instruktion påträffas) hoppar den tillbaka till den adressen som sparades undan och programmet kan fortsätta köras därifrån funktionsanropet gjordes.

En funktion kan skrivas så att den förväntar sig en eller flera parametrar från programmet som anropar den, och den kan även returnera ett resultat. Några exempel är en slumptalsfunktion, eller en funktion som räknar ut kvadratroten ur ett tal. Många funktioner är just rent matematiska funktioner. Men det finns även funktioner med sidoeffekter, sådana som stänger av eller pausar programmet, ändra data i minnet eller läser eller skriver till en fil. Just sådana funktioner karaktäriserar imperativ programmering.

Funktioner kan även anropa sig själva, vilket kallas rekursion. En sådan funktion tillåter direkt implementation av matematisk induktion och söndra och härska-algoritmer. Några typiska exempel på detta är sorteringsalgoritmer och fakultetberäkning.

En funktion vars enda syfte är att returnera ett sanningsvärde: sant eller falskt, brukar kallas predikatfunktion.

Självmodifierande kod

[redigera | redigera wikitext]

Innan anropsinstruktioner fanns fick man göra funktioner med hjälp av självmodifierande kod. Innan man hoppade till adressen där funktionskoden låg, lät man en instruktion ändra återhoppsinstruktionen i slutet av funktionens kropp så att den skulle hoppa tillbaka till rätt adress när den var klar.

Anropsstacken

[redigera | redigera wikitext]

Moderna implementationer använder en anropsstack för att hålla reda på returadresser när funktioner anropas av andra funktioner. När en funktion anropas sparas returadressen överst i stacken, och om ytterligare ett funktionsanrop görs inne i denna funktion kommer en ny returadress att läggas ovanpå, det vill säga överst i stacken. När en funktion är klar kommer programmet att returnera till adressen överst i stacken.

Inom objektorienterad programmering är en metod en funktion som antingen tillhör en klass (klassmetod) eller en instans (instansmetod).

Virtuell funktion

[redigera | redigera wikitext]

En virtuell funktion eller virtuell metod en funktion som definieras i en klass som kan bli omdefinierad (överskriven) av subklasser, även ifall den statiska typen av ett objekt inte är densamma som den faktiska typen.

I en del språk som till exempel C++, C# och Object Pascal måste en klass medlemsfunktioner uttryckligen deklareras som virtuella. I andra språk som Ruby och Java är alla medlemsfunktioner virtuella.

För virtuella funktioner kan hoppadressen för ett anrop inte avgöras under kompileringen eftersom den exakta typen av det objekt som är föremål för anropet inte är känt. Hoppet blir alltså indirekt, vilket ger en liten prestandaförsämring.

I nedanstående C++-exempel har vi en klasshierarki baserad på klassen Djur, med en virtuell funktion lukta() som skriver ut hur djuret luktar. I huvudprogrammet instansierar vi ett objekt av vardera klassen och anropar lukta().

 #include <iostream>  using namespace std;    class Djur  {  public:     virtual void lukta() { cout << "Jag luktar som ett vanligt djur." << endl; }  };    class Gris : public Djur  {  public:     void lukta() { cout << "Jag luktar som en gris." << endl; }  };    class Fisk : public Djur  {  public:     void lukta() { cout << "Jag luktar som en fisk." << endl; }  };    class AnnatDjur : public Djur  {  };    int main()  {     Djur *djur[4];     djur[0] = new Djur();     djur[1] = new Gris();     djur[2] = new Fisk();     djur[3] = new AnnatDjur();       for (int i = 0; i < 4; i++)        djur[i]->lukta();       return 0;  } 

Här är resultatet av en körning:

Jag luktar som ett vanligt djur. Jag luktar som en gris. Jag luktar som en fisk. Jag luktar som ett vanligt djur. 

Trots att huvudprogrammet inte vet att djur[1] pekar på ett objekt av typen Gris – variabelns statiska typ är alltjämt Djur – så har uppenbarligen koden i klassen Gris exekverats. Detta hade inte hänt ifall lukta() inte hade markerats som virtuell; då hade i samtliga fall funktionen i basklassen Djur exekverats.